(-2147483648> 0) zwraca true w C ++?

241

-2147483648 jest najmniejszą liczbą całkowitą dla typu liczb całkowitych z 32 bitami, ale wydaje się, że przepełni się w if(...)zdaniu:

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

Zostanie to wydrukowane truepodczas moich testów. Jeśli jednak użyjemy -2147483648 na liczbę całkowitą, wynik będzie inny:

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

To zostanie wydrukowane false.

Jestem zmieszany. Czy ktoś może wyjaśnić to?


Aktualizacja 02-05-2012:

Dzięki za komentarze, w moim kompilatorze rozmiar int to 4 bajty. Używam VC do prostych testów. Zmieniłem opis w moim pytaniu.

To wiele bardzo dobrych odpowiedzi w tym poście, AndreyT udzielił bardzo szczegółowego wyjaśnienia, w jaki sposób kompilator będzie zachowywał się na takich danych wejściowych i jak zaimplementowano tę minimalną liczbę całkowitą. Z drugiej strony qPCR4vir dał pewne powiązane „osobliwości” i sposób reprezentowania liczby całkowitej. Tak imponujące!

benyl
źródło
48
„wszyscy wiemy, że -2147483648 jest najmniejszą liczbą całkowitą” To zależy od wielkości liczby całkowitej.
orlp
14
„wszyscy wiemy, że -2147483648 jest najmniejszą liczbą całkowitą” - Myślałem, że nie było najmniejszej liczby całkowitej, ponieważ jest ich nieskończenie wiele… Cokolwiek.
@Inisheer Z 4 Byte całkowitymi może masz INT_MINz -9223372036854775808, jeśli CHAR_BITjest 16. A nawet CHAR_BIT == 8i sizeof(int== 4) `może pojawić się -9223372036854775807, ponieważ C nie wymagają 2-Uzupełnienia liczb.
12431234123412341234123,

Odpowiedzi:

391

-2147483648nie jest „liczbą”. Język C ++ nie obsługuje ujemnych wartości literałów.

-2147483648jest właściwie wyrażeniem: dodatnią wartością dosłowną 2147483648z jednoznacznym -operatorem przed nim. Wartość 2147483648jest najwyraźniej za duża dla pozytywnej strony intzasięgu na twojej platformie. Gdyby typ long intmiał większy zasięg na twojej platformie, kompilator musiałby automatycznie założyć, że 2147483648ma on long inttyp. (W C ++ 11 kompilator musiałby również wziąć pod uwagę long long inttyp.) Sprawiłoby to, że kompilator oceniałby -2147483648w domenie większego typu, a wynik byłby ujemny, jak można by się spodziewać.

Jednak najwyraźniej w twoim przypadku zasięg long intjest taki sam jak zasięg inti ogólnie nie ma typu całkowitego o większym zasięgu niż intna twojej platformie. To formalnie oznacza, że ​​dodatnia stała 2147483648przepełnia wszystkie dostępne typy liczb całkowitych ze znakiem, co z kolei oznacza, że ​​zachowanie twojego programu jest niezdefiniowane. (To trochę dziwne, że specyfikacja języka wybiera w takich przypadkach niezdefiniowane zachowanie, zamiast wymagać komunikatu diagnostycznego, ale tak właśnie jest.)

W praktyce, biorąc pod uwagę, że zachowanie jest niezdefiniowane, 2147483648może zostać zinterpretowane jako pewna ujemna wartość zależna od implementacji, która zmienia się w dodatnią po -zastosowaniu do niej jednostronnej wartości . Alternatywnie, niektóre implementacje mogą podjąć decyzję o próbie użycia niepodpisanych typów do przedstawienia wartości (na przykład w kompilatorach C89 / 90 było to wymagane unsigned long int, ale nie w C99 lub C ++). Implementacje mogą robić wszystko, ponieważ i tak zachowanie jest niezdefiniowane.

Na marginesie, jest to powód, dla którego stałe takie INT_MINsą zwykle definiowane jako

#define INT_MIN (-2147483647 - 1)

zamiast pozornie prostszego

#define INT_MIN -2147483648

Ten ostatni nie działałby zgodnie z przeznaczeniem.

Mrówka
źródło
78
Jest to również, dlaczego odbywa się to: #define INT_MIN (-2147483647 - 1).
orlp
5
@ RichardJ.RossIII - z clang prawdopodobnie dostajesz literał 64-bitowy, ponieważ był zbyt duży, aby zmieścić się w nim int. Implementacja OP może nie mieć typu 64-bitowego.
Carl Norum
1
@ RichardJ.RossIII: Uważam, że to zachowanie jest zdefiniowane / niezdefiniowane w implementacji.
Oliver Charlesworth,
3
Nigdy nie myślałem, że „liczba ujemna” nie jest analizowana jako taka. Nie widzę powodu. Mam nadzieję, że -1.0jest to analizowane jako podwójna ujemna wartość, prawda?
leemes
6
@ qPCR4vir: Nie. Jak napisałem w komentarzu do twojej odpowiedzi, ani współczesne C, ani C ++ nie zezwalają w tym przypadku na używanie typów niepodpisanych (z nieosiągniętą stałą dziesiętną ). Tylko pierwszy standardowy C (C89 / 90) dozwolony unsigned long intw tym kontekście, ale w C99 to zezwolenie zostało usunięte. Nienaprawione literały w C i C ++ muszą mieć podpisane typy. Jeśli zobaczysz tutaj niepodpisany typ, w którym zadziała podpisany, oznacza to, że Twój kompilator jest uszkodzony. Jeśli zobaczysz tutaj niepodpisany typ, gdy żaden podpisany typ nie zadziała, to jest to tylko szczególny przejaw niezdefiniowanego zachowania.
AnT
43

Kompilator (VC2012) promuje do „minimalnych” liczb całkowitych, które mogą przechowywać wartości. W pierwszym przypadku signed int(i long int) nie może (przed zastosowaniem znaku), ale unsigned intmoże: 2147483648maunsigned int ???? rodzaj. W drugiej wymuszasz intz unsigned.

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

ostrzeżenie C4146: operator jednoargumentowy minus zastosowany do typu niepodpisanego , wynik wciąż niepodpisany

Oto powiązane „osobliwości”:

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

Standard C ++ 11 :

2.14.2 Literały całkowite [lex.icon]

Literał całkowity to ciąg cyfr, który nie ma kropki ani wykładnika. Literał całkowity może mieć prefiks określający jego podstawę i sufiks określający jego typ.

Typ literału liczb całkowitych jest pierwszym z odpowiedniej listy, na której można przedstawić jego wartość.

wprowadź opis zdjęcia tutaj

Jeśli literału liczb całkowitych nie można przedstawić żadnym typem na liście, a rozszerzony typ liczb całkowitych (3.9.1) może reprezentować jego wartość, może mieć ten rozszerzony typ liczb całkowitych. Jeżeli wszystkie typy literału z listy są podpisane, należy podpisać rozszerzony typ liczb całkowitych. Jeżeli wszystkie typy literału na liście są niepodpisane, rozszerzona liczba całkowita nie będzie podpisana. Jeśli lista zawiera zarówno podpisane, jak i niepodpisane typy, rozszerzona liczba całkowita może być podpisana lub niepodpisana. Program jest źle sformułowany, jeśli jedna z jego jednostek tłumaczeniowych zawiera literał całkowity, którego nie może reprezentować żaden z dozwolonych typów.

I to są zasady promocji dla liczb całkowitych w standardzie.

4.5 Promocje integralne [conv.prom]

Prvalue od typu całkowitych innych niż bool, char16_t, char32_tlub wchar_tktórej całkowita konwersja stopień (4,13) jest mniejszy niż stopień Int można przekształcić do prvalue typu intczy intmoże oznaczać wszystkie wartości typu źródłowego; w przeciwnym razie wartość źródłowa może zostać przekonwertowana na wartość typu unsigned int.

qPCR4vir
źródło
3
@ qPCR4vir: W C89 / 90 kompilatory miały typów ruchu int, long int, unsigned long intdo reprezentowania unsuffixed stałe dziesiętne. To był jedyny język, który pozwalał używać niepodpisanych typów dla niefiksowanych stałych dziesiętnych. W C ++ 98 było to intlub long int. Niedozwolone typy bez znaku. Ani C (począwszy od C99), ani C ++ nie pozwala kompilatorowi na używanie typów niepodpisanych w tym kontekście. Twój kompilator może oczywiście używać typów niepodpisanych, jeśli żaden z podpisanych nie działa, ale nadal jest to tylko szczególny przejaw nieokreślonego zachowania.
AnT
@AndreyT. Wspaniały! Oczywiście, twoja siła. Czy VC2012 jest zepsuty?
qPCR4vir
@ qPCR4vir: AFAIK, VC2012 nie jest kompilator C ++ 11 jeszcze (to jest?), co oznacza, że musi skorzystać z jednej intlub long intdo reprezentowania 2147483648. AFAIK, zarówno w VC2012, jak inti long int32-bitowym. Oznacza to, że w VC2012 dosłowność 2147483648powinna prowadzić do nieokreślonego zachowania . Gdy zachowanie jest niezdefiniowane, kompilator może robić wszystko. Oznaczałoby to, że VC2012 nie jest uszkodzony. Po prostu wydał mylący komunikat diagnostyczny. Zamiast powiedzieć ci, że zachowanie jest całkowicie nieokreślone, postanowił użyć typu bez znaku.
AnT
@AndreyT: Czy mówisz, że kompilatory mogą emitować demony nosowe, jeśli kod źródłowy zawiera niefiksowany literał dziesiętny, który przekracza maksymalną wartość ze znaku longi nie jest wymagany do wydania komunikatu diagnostycznego? To wydawałoby się zepsute.
supercat
To samo „ostrzeżenie C4146” w VS2008 i „ta stała dziesiętna jest niepodpisana tylko w ISO C90” w G ++
spyder
6

Krótko mówiąc, 2147483648przepełnia się -2147483648i (-(-2147483648) > 0)jest true.

Tak to 2147483648wygląda w systemie binarnym.

Ponadto w przypadku podpisanych obliczeń binarnych najbardziej znaczącym bitem („MSB”) jest bit znaku. To pytanie może pomóc wyjaśnić dlaczego.

drzymala
źródło
4

Ponieważ w -2147483648rzeczywistości zastosowano do niego 2147483648negację ( -), liczba nie jest taka, jak można się spodziewać. W rzeczywistości jest to odpowiednik tego pseudokodu:operator -(2147483648)

Teraz, zakładając, że twój kompilator ma wartość sizeof(int)równą 4i CHAR_BITjest zdefiniowany jako 8, to spowoduje, że 2147483648przepełnienie będzie maksymalną podpisaną wartością liczby całkowitej ( 2147483647). Więc jaka jest maksymalna plus jedna? Pozwala to sprawdzić z 4-bitową liczbą całkowitą komplement 2s.

Czekać! 8 przepełnia liczbę całkowitą! Co robimy? Użyj jego niepodpisanej reprezentacji 1000i interpretuj bity jako liczbę całkowitą ze znakiem. Ta reprezentacja pozostawia nam -8zastosowanie negacji dopełniacza 2s 8, co, jak wszyscy wiemy, jest większe niż 0.

Właśnie dlatego <limits.h>(i <climits>) zwykle definiuje się INT_MINjako ((-2147483647) - 1)- tak, że maksymalna liczba całkowita ze znakiem ( 0x7FFFFFFF) jest negowana ( 0x80000001), a następnie zmniejszana ( 0x80000000).

Cole Johnson
źródło
W przypadku liczby 4-bitowej negacja dopełniacza dwóch -8jest nadal -8.
Ben Voigt,
Tyle że -8 jest interpretowane jako 0-8, a nie ujemne 8. I 8 przepełnia 4-bitowy znak int
Cole Johnson
Zastanów się, -(8)która w C ++ jest taka sama jak -8- jest to negacja zastosowana do literału, a nie negatywna literał. Dosłowne jest to 8, że nie pasuje do 4-bitowej liczby całkowitej ze znakiem, więc musi być bez znaku. Wzór jest 1000. Jak dotąd twoja odpowiedź jest prawidłowa. Negacja uzupełnienia dwóch 1000w 4 bitach jest taka 1000, że nie ma znaczenia, czy jest podpisana czy niepodpisana. Twoja odpowiedź mówi „interpretuj bity jako liczbę całkowitą ze znakiem”, która tworzy wartość -8po negacji dopełniacza dwóch, tak jak przed negacją.
Ben Voigt,
Oczywiście w „4-bitowym C ++” nie ma „interpretować bity jako liczbę całkowitą ze znakiem”. Literał staje się najmniejszym typem, który może go wyrazić, czyli 4-bitową liczbą całkowitą bez znaku . Wartość literału to 8. Stosuje się negację (moduł 16), co daje ostateczną odpowiedź na 8. Kodowanie to wciąż 1000, ale wartość jest inna, ponieważ wybrano typ bez znaku.
Ben Voigt,