Подписанные/неподписанные сравнения

Я пытаюсь понять, почему следующий код не выдает предупреждение в указанном месте.

//from limits.h
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define INT_MAX  2147483647 /* maximum (signed) int value */
            /* = 0x7fffffff */

int a = INT_MAX;
//_int64 a = INT_MAX; // makes all warnings go away
unsigned int b = UINT_MAX;
bool c = false;

if(a < b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a > b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a <= b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a >= b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a == b) // no warning <--- warning expected here
    c = true;
if(((unsigned int)a) == b) // no warning (as expected)
    c = true;
if(a == ((int)b)) // no warning (as expected)
    c = true;

Я думал, что это связано с продвижением фона, но последние два, похоже, говорят иначе.

На мой взгляд, первое сравнение == - такое же несоответствие подписанного/неподписанного, как и остальные?

Ответ 1

При сравнении подписанного с unsigned компилятор преобразует значение signed в unsigned. Для равенства это не имеет значения, -1 == (unsigned) -1. Для других сравнений это имеет значение, например. верно следующее: -1 > 2U.

EDIT: Ссылки:

5/9: (выражения)

Многие бинарные операторы, которые ожидают операнды арифметики или перечисления тип вызывает конверсии и доходность аналогичным образом. Цель состоит в том, чтобы дать общий тип, который также является типом результата. Этот шаблон называется обычным арифметические преобразования, которые определяется следующим образом:

  • Если либо операнд имеет тип long double, другие должны быть преобразованы в длинные двойной.

  • В противном случае, если либо операнд двойное, другое должно быть преобразован в двойной.

  • В противном случае, если либо операндом является float, другой преобразуется в float.

  • В противном случае интегральные акции (4.5) должны выполняться на обоих операнды .54)

  • Затем, если либо операнд долгое время без знака преобразуется в unsigned long.

  • В противном случае, если один операнд длинный int и другой неподписанный int, тогда если длинный int может представлять все значения беззнакового int, unsigned int преобразуется в long int; в противном случае оба операнда должны быть преобразованы в unsigned long внутр.

  • В противном случае, если любой из операндов долго, другой должен быть преобразован в длинный.

  • В противном случае, если либо операнд является неподписанным, другое должно быть преобразуется в unsigned.

4.7/2: (Интегральные преобразования)

Если тип назначения не указан, результирующее значение является наименьшим беззнаковое целое, совпадающее с (по модулю 2 n где n равно количество бит, используемых для представления неподписанный тип). [Примечание: в два раза дополнение, это преобразование является концептуальным, и существует никаких изменений в битовой схеме (если есть не является усечением). ]

EDIT2: уровни предупреждения MSVC

О том, что предупреждают о различных уровнях предупреждения MSVC, является, конечно же, выбор разработчиков. Как я вижу, их выбор в отношении подписанного/неподписанного равенства по сравнению с большим/меньшим сравнением имеет смысл, это, конечно, полностью субъективно:

-1 == -1 означает то же самое, что и -1 == (unsigned) -1 - я нахожу этот интуитивный результат.

-1 < 2 не означает то же самое, что и -1 < (unsigned) 2. Это на первый взгляд менее интуитивно понятно, и IMO заслуживает "более раннего" предупреждения.

Ответ 2

Почему подписанные/неподписанные предупреждения важны, и программисты должны обратить на них внимание, это продемонстрировано в следующем примере.

Угадайте вывод этого кода?

#include <iostream>

int main() {
        int i = -1;
        unsigned int j = 1;
        if ( i < j ) 
            std::cout << " i is less than j";
        else
            std::cout << " i is greater than j";

        return 0;
}

Вывод:

i is greater than j

Удивлены? Демо-версия: http://www.ideone.com/5iCxY

Нижняя линия: для сравнения, если один операнд unsigned, то другой операнд неявно преобразуется в unsigned , если его тип подписан!

Ответ 3

Оператор == только выполняет поразрядное сравнение (простым делением, чтобы увидеть, равно ли оно 0).

Чем меньше/больше сравнений, тем больше на знаке числа.

4 бит Пример:

1111 = 15? или -1?

так что если у вас 1111 < 0001... это двусмысленно...

но если у вас 1111 == 1111... Это то же самое, хотя вы не имели в виду это.

Ответ 4

В системе, которая представляет значения с использованием 2-дополнения (большинство современных процессоров), они равны даже в их двоичной форме. Возможно, поэтому компилятор не жалуется на a == b.

И мне этот странный компилятор не предупреждает вас о a == ((int) b). Я думаю, что это должно дать вам предупреждение об усечении целых чисел или что-то в этом роде.