Подписанные / неподписанные сравнения - PullRequest
75 голосов
/ 24 марта 2011

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

//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;

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

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

Ответы [ 4 ]

79 голосов
/ 24 марта 2011

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

РЕДАКТИРОВАТЬ: Ссылки:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

27 голосов
/ 24 марта 2011

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

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

#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 голосов
/ 24 марта 2011

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

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

4 бита Пример:

1111 = 15? или -1?

так что если у вас 1111 <0001 ... это неоднозначно ... </p>

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

1 голос
/ 24 марта 2011

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

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

...