(-2147483648> 0) возвращает true в C ++?

241

-2147483648 - это наименьшее целое число для целочисленного типа с 32 битами, но кажется, что оно будет переполнено в if(...)предложении:

if (-2147483648 > 0)
    std::cout << "true";
else
    std::cout << "false";

Это напечатает trueв моем тестировании. Однако, если мы приведем -2147483648 к целому числу, результат будет другим:

if (int(-2147483648) > 0)
    std::cout << "true";
else
    std::cout << "false";

Это напечатает false.

Я запутался. Кто-нибудь может дать объяснение по этому поводу?


Обновление 02-05-2012:

Спасибо за ваши комментарии, в моем компиляторе размер int составляет 4 байта. Я использую VC для простого тестирования. Я изменил описание в моем вопросе.

Это очень много хороших ответов в этом посте. AndreyT дал очень подробное объяснение того, как компилятор будет вести себя при таком вводе, и как реализовано это минимальное целое число. qPCR4vir, с другой стороны, дал некоторые связанные «курьезы» и то, как представлены целые числа. Так впечатляет!

benyl
источник
48
«Мы все знаем, что -2147483648 является наименьшим числом целого числа» Это зависит от размера целого числа.
orlp
14
«Мы все знаем, что -2147483648 - это наименьшее число целых чисел», - я подумал, что не было наименьшего целого числа, поскольку их бесконечно много ... Что угодно.
@Inisheer С 4 байта целые числа , вы можете иметь INT_MINо -9223372036854775808, если CHAR_BITесть 16. И даже CHAR_BIT == 8и sizeof(int== 4) `вы можете получить , -9223372036854775807потому что C не требуется 2-комплемента номера.
12431234123412341234123

Ответы:

391

-2147483648это не "число". Язык C ++ не поддерживает отрицательные литеральные значения.

-2147483648на самом деле выражение: положительное буквальное значение 2147483648с унарным -оператором перед ним. Значение 2147483648слишком велико для положительной стороны intдиапазона на вашей платформе. Если тип long intимеет больший диапазон на вашей платформе, компилятор должен будет автоматически предположить, что он 2147483648имеет long intтип. (В C ++ 11 компилятору также придется учитывать long long intтип.) Это заставит компилятор вычислять -2147483648в области большего типа, и результат будет отрицательным, как и следовало ожидать.

Однако, по-видимому, в вашем случае диапазон long intсовпадает с диапазоном int, и в целом нет целочисленного типа с большим диапазоном, чем intна вашей платформе. Формально это означает, что положительная константа 2147483648переполняет все доступные целочисленные типы со знаком, что, в свою очередь, означает, что поведение вашей программы не определено. (Немного странно, что спецификация языка в таких случаях выбирает неопределенное поведение, вместо того, чтобы требовать диагностическое сообщение, но это так.)

На практике, принимая во внимание, что поведение не определено, 2147483648может интерпретироваться как некое зависящее от реализации отрицательное значение, которое становится положительным после -применения к нему унарного кода. Альтернативно, некоторые реализации могут решить попытаться использовать неподписанные типы для представления значения (например, в C89 / 90 компиляторы должны были использоваться unsigned long int, но не в C99 или C ++). Реализациям разрешено делать что угодно, поскольку поведение в любом случае не определено.

В качестве примечания, это причина, почему такие константы, как INT_MINправило, определяются как

#define INT_MIN (-2147483647 - 1)

вместо, казалось бы, более простой

#define INT_MIN -2147483648

Последний не будет работать как задумано.

Муравей
источник
78
Это также объясняет , почему это делается: #define INT_MIN (-2147483647 - 1).
orlp
5
@ RichardJ.RossIII - с помощью clang вы, вероятно, получаете литерал с 64-битной типизацией, поскольку он был слишком большим, чтобы уместиться в int. Реализация OP может не иметь 64-битного типа.
Карл Норум
1
@ RichardJ.RossIII: я считаю, что это поведение определяется реализацией / не определено.
Оливер Чарльзуорт,
3
Я никогда не думал, что «отрицательное число» не анализируется как таковое. Я не вижу причины. Я надеюсь, что -1.0это анализируется как отрицательное двойное значение, не так ли?
Leemes
6
@ qPCR4vir: Нет. Как я уже писал в своем комментарии к вашему ответу, ни современные C, ни C ++ не позволяют использовать в этом случае типы без знака (с десятичной постоянной без суффикса ). Только первый стандарт C (C89 / 90) разрешен unsigned long intв этом контексте, но в C99 это разрешение было удалено. Обязательные литералы в C и C ++ должны иметь подписанные типы. Если вы видите здесь беззнаковый тип, когда подписанный будет работать, это означает, что ваш компилятор не работает. Если вы видите здесь неподписанный тип, когда никакой подписанный тип не будет работать, то это просто конкретное проявление неопределенного поведения.
13
43

Компилятор (VC2012) повышает до "минимальных" целых чисел, которые могут содержать значения. В первом случае signed intlong int) не может (до применения знака), но unsigned intможет: 2147483648имеетunsigned int ???? тип. Во втором вы вынуждаете intот unsigned.

const bool i= (-2147483648 > 0) ;  //   --> true

предупреждение C4146: унарный оператор минус применяется к типу без знака , результат все еще без знака

Здесь связаны "курьезы":

const bool b= (-2147483647      > 0) ; //  false
const bool i= (-2147483648      > 0) ; //  true : result still unsigned
const bool c= ( INT_MIN-1       > 0) ; //  true :'-' int constant overflow
const bool f= ( 2147483647      > 0) ; //  true
const bool g= ( 2147483648      > 0) ; //  true
const bool d= ( INT_MAX+1       > 0) ; //  false:'+' int constant overflow
const bool j= ( int(-2147483648)> 0) ; //  false : 
const bool h= ( int(2147483648) > 0) ; //  false
const bool m= (-2147483648L     > 0) ; //  true 
const bool o= (-2147483648LL    > 0) ; //  false

Стандарт C ++ 11 :

2.14.2 Целочисленные литералы [lex.icon]

...

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

...

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

введите описание изображения здесь

Если целочисленный литерал не может быть представлен каким-либо типом в его списке, и расширенный целочисленный тип (3.9.1) может представлять его значение, он может иметь этот расширенный целочисленный тип. Если все типы в списке для литерала подписаны, расширенный целочисленный тип должен быть подписан. Если все типы в списке для литерала не подписаны, расширенный целочисленный тип должен быть без знака. Если список содержит типы со знаком и без знака, расширенный целочисленный тип может быть как со знаком, так и без знака. Программа плохо сформирована, если один из ее модулей перевода содержит целочисленный литерал, который не может быть представлен ни одним из разрешенных типов.

И это правила продвижения для целых чисел в стандарте.

4.5 Интегральные акции [conv.prom]

Prvalue целого типа, кроме bool, char16_t, char32_tили wchar_tчье число преобразования ранга (4.13) меньше , чем ранг Int может быть преобразован в prvalue типа , intесли intможет представлять все значения типа источника; в противном случае исходное значение prvalue может быть преобразовано в значение типа prvalue unsigned int.

qPCR4vir
источник
3
@ qPCR4vir: В C89 / 90 составителей должны были виды использования int, long int, unsigned long intчтобы представлять unsuffixed десятичных констант. Это был единственный язык, который позволял использовать неподписанные типы для нефиксированных десятичных констант. В C ++ 98 это было intили long int. Не допускается использование неподписанных типов. Ни C (начиная с C99), ни C ++ не позволяют компилятору использовать неподписанные типы в этом контексте. Ваш компилятор, конечно, может свободно использовать неподписанные типы, если ни один из подписанных не работает, но это все еще лишь конкретное проявление неопределенного поведения.
13
@AndreyT. Большой! Конечно, ваша правота. VC2012 не работает?
qPCR4vir
@ qPCR4vir: AFAIK, VC2012 не C ++ 11 компилятор еще (это?), что означает , что он должен использовать либо intили long intпредставлять 2147483648. Также, AFAIK, в VC2012 оба intи long int32-битные типы. Это означает, что в VC2012 литерал 2147483648должен вести к неопределенному поведению . Когда поведение не определено, компилятору разрешено делать все что угодно. Это будет означать, что VC2012 не сломан. Он просто выдал вводящее в заблуждение диагностическое сообщение. Вместо того, чтобы говорить вам, что поведение не определено, он решил использовать тип без знака.
13
@AndreyT: Вы говорите, что компиляторы могут свободно генерировать носовые демоны, если исходный код содержит десятичный литерал без суффикса, который превышает максимальное значение со знаком long , и не требует выдачи диагностики? Это казалось бы сломанным.
суперкат
То же самое «предупреждение C4146» в VS2008 и «эта десятичная константа не подписана только в ISO C90» в G ++
spyder
6

Короче говоря, 2147483648переполняет -2147483648, и (-(-2147483648) > 0)есть true.

Это то , как 2147483648выглядит в двоичной форме .

Кроме того, в случае двоичных вычислений со знаком наиболее значимым битом («MSB») является знаковый бит. Этот вопрос может помочь объяснить почему.

drzymala
источник
4

Поскольку -2147483648на самом деле к нему применяется 2147483648negation ( -), число не соответствует ожидаемому. На самом деле это эквивалент этого псевдокода:operator -(2147483648)

Теперь, если предположить, что ваш компилятор sizeof(int)равен 4и CHAR_BITопределен как 8, это сделает 2147483648переполнение максимальным знаковым значением целого числа (2147483647 ). Так какой же максимум плюс один? Давайте разберемся с 4-битным целым числом комплимента 2s.

Подождите! 8 переполняет целое число! Что мы делаем? Используйте его беззнаковое представление 1000и интерпретируйте биты как целое число со знаком. Это представление оставляет нам -8применение отрицания дополнения 2s 8, которое, как мы все знаем, больше, чем 0.

Вот почему <limits.h><climits>) обычно определяют INT_MINкак ((-2147483647) - 1)- так, что максимальное целое число со знаком ( 0x7FFFFFFF) отрицается ( 0x80000001), а затем уменьшается ( 0x80000000).

Коул Джонсон
источник
Для 4-битного числа отрицание дополнения до двух по- -8прежнему -8.
Бен Фойгт
За исключением того, что -8 интерпретируется как 0-8, а не как отрицательное 8. И 8 переполняет 4-битное
Коул Джонсон
Рассмотрим, -(8)что в C ++ - это то же самое, -8что отрицание применяется к литералу, а не к отрицательному литералу. Литерал is 8, который не помещается в 4-разрядное целое число со знаком, поэтому он должен быть без знака. Шаблон есть 1000. Пока ваш ответ правильный. Отрицание дополнения к двум 1000в 4 битах 1000, не имеет значения, подписано оно или нет. Ваш ответ гласит: «интерпретируйте биты как целое число со знаком», которое задает значение -8после отрицания дополнения к двум, как это было до отрицания.
Бен Фойгт
Конечно, в «4-битном C ++» нет «интерпретировать биты как целочисленный шаг со знаком». Литерал становится наименьшим типом, который может его выразить. Это 4-битное целое число без знака . Значение литерала есть 8. Отрицание применяется (по модулю 16), в результате чего окончательный ответ 8. Кодировка все еще 1000, но значение отличается, потому что был выбран тип без знака.
Бен Фойгт