(-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, з іншого боку, дав деякі пов'язані "цікавості" та те, як представлені цілі числа. Так вражаюче!


48
"всі ми знаємо, що -2147483648 - найменше число цілого числа". Це залежить від величини цілого числа.
orlp

14
"всі ми знаємо, що -2147483648 - найменша кількість цілого числа", - я подумав, що немає найменшого цілого числа, оскільки їх нескінченно багато ... Що б там не було.

@Inisheer З чотирма байтовими цілими числами у вас може бути 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 - з клангом ви, ймовірно, отримуєте 64-розрядний буквар, оскільки він був занадто великий, щоб вмістити його int. Реалізація ОП може не мати 64-бітного типу.
Карл Норум

1
@ RichardJ.RossIII: Я вважаю, що така поведінка є визначеною / невизначеною реалізацією.
Олівер Чарльворт

3
Я ніколи не думав, що "негативне число" не розбирається як таке. Я не бачу причини. Я сподіваюся, що -1.0це розбирається як негативне подвійне значення, чи не так?
leemes

6
@ qPCR4vir: Ні. Як я писав у коментарі до вашої відповіді, ні сучасні C, ні C ++ не дозволяють використовувати в цьому випадку неподписані типи (з нефіксованою десятковою константою ). unsigned long intУ цьому контексті дозволений лише перший стандарт C (C89 / 90) , але в C99 цей дозвіл було видалено. Для нефіксованих літералів в C і C ++ потрібно мати типи підписів . Якщо ви бачите тут неподписаний тип, коли підписаний працював би, це означає, що ваш компілятор зламаний. Якщо ви бачите тут неподписаний тип, коли жоден підписаний тип не працював, це лише специфічний прояв невизначеної поведінки.
2013 року

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може представляти всі значення типу джерела; в іншому випадку джерело первинного значення може бути перетворене в первісне значення типу unsigned int.


3
@ QPCR4vir: У C89 / 90 укладачів повинні були види використання int, long int, unsigned long intщоб представляти unsuffixed десяткових констант. Це була єдина мова, яка дозволяла використовувати неподписані типи для нефіксованих десяткових констант. У С ++ 98 це було intабо long int. Не допускаються неподписані типи. Ні C (починаючи з C99), ні C ++ не дозволяють компілятору використовувати неподписані типи в цьому контексті. Звичайно, ваш компілятор може використовувати безпідписані типи, якщо жоден із підписаних не працює, але це все-таки лише специфічний прояв невизначеної поведінки.
ANT

@AndreyT Чудово! З вашої уваги, ваша правда. Вирваний VC2012?
qPCR4vir

@ qPCR4vir: AFAIK, VC2012 ще не є компілятором C ++ 11 (так?), що означає, що він повинен використовувати або int або long intпредставляти 2147483648. Також AFAIK, у VC2012 обидва intі long intє 32-бітовими типами. Це означає, що у VC2012 буквальне 2147483648повинно призвести до невизначеної поведінки . Коли поведінка не визначена, компілятору дозволяється робити що завгодно. Це означало б, що VC2012 не порушено. Він просто видав оманливе діагностичне повідомлення. Замість того, щоб сказати вам, що поведінка не відповідає визначенню, він вирішив використовувати неподписаний тип.
ANT

@AndreyT: Ви хочете сказати, що компілятори вільно випускають носових демонів, якщо вихідний код містить нефіксований десятковий буквар, який перевищує максимальне значення підписаного long, і не потрібно видавати діагностику? Це здавалося б зламаним.
supercat

Те саме "попередження C4146" у VS2008 та "ця десяткова константа не підписана лише в ISO C90" у G ++
шпигун

6

Коротше кажучи, 2147483648переповнює до -2147483648та (-(-2147483648) > 0)є true.

Це те , як 2147483648виглядає в двійковій формі .

Крім того, у випадку підписаних двійкових обчислень найбільш значущим бітом ("MSB") є бітовий знак. Це питання може допомогти пояснити, чому.


4

Тому що -2147483648 насправді застосовано 2147483648заперечення ( -), число не те, що ви очікували. Це фактично еквівалент цього псевдокоду:operator -(2147483648)

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

Зачекайте! 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-бітний підписаний int
Cole Johnson

Поміркуйте, -(8)що в C ++ те саме, що -8- це заперечення, застосоване до буквального, а не негативного літералу. Буквал є 8, який не вписується в підписане 4-бітове ціле число, тому він повинен бути непідписаним. Візерунок є 1000. Поки ваша відповідь правильна. Заперечення комплементу двох 1000у чотирьох бітах - 1000це не має значення, підписано чи непідписано. У Вашій відповіді сказано: "інтерпретуйте біти як ціле підписане число", яке робить значення -8після заперечення комплементу обох, як і раніше заперечення.
Бен Войгт

Звичайно, в "4-бітовому C ++" немає "інтерпретувати біти як підписаний цілий крок". Буквал стає найменшим типом, який може його виразити, а це непідписане 4-бітове ціле число . Значення буквеного є 8. Застосовується заперечення (модуль 16), що призводить до остаточної відповіді 8. Кодування все ще 1000, але значення інше, оскільки обрано непідписаний тип.
Ben Voigt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.