(-2147483648> 0) gibt in C ++ true zurück?

241

-2147483648 ist die kleinste Ganzzahl für den Ganzzahltyp mit 32 Bit, aber es scheint, dass sie im if(...)Satz überläuft :

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

Dies wird truein meinen Tests gedruckt . Wenn wir jedoch -2147483648 in eine Ganzzahl umwandeln, ist das Ergebnis anders:

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

Dies wird gedruckt false.

Ich bin verwirrt. Kann jemand eine Erklärung dazu geben?


Update 02-05-2012:

Vielen Dank für Ihre Kommentare. In meinem Compiler beträgt die Größe von int 4 Byte. Ich benutze VC für einige einfache Tests. Ich habe die Beschreibung in meiner Frage geändert.

Das sind viele sehr gute Antworten in diesem Beitrag. AndreyT hat sehr ausführlich erklärt, wie sich der Compiler bei solchen Eingaben verhält und wie diese minimale Ganzzahl implementiert wurde. qPCR4vir gab andererseits einige verwandte "Kuriositäten" an und wie Ganzzahlen dargestellt werden. Sehr beeindruckend!

Benyl
quelle
48
"Wir alle wissen, dass -2147483648 die kleinste Anzahl von Ganzzahlen ist." Das hängt von der Größe der Ganzzahl ab.
Orlp
14
"Wir alle wissen, dass -2147483648 die kleinste Anzahl von Ganzzahlen ist" - Ich dachte, dass es keine kleinste Ganzzahl gibt, da es unendlich viele von ihnen gibt ... Wie auch immer.
@Inisheer Mit 4-Byte-Ganzzahlen können Sie eine INT_MINvon haben -9223372036854775808, wenn sie CHAR_BIT16 ist. Und selbst mit CHAR_BIT == 8und sizeof(int== 4) `erhalten Sie möglicherweise, -9223372036854775807weil C keine 2-Komplement-Zahlen benötigt.
12431234123412341234123

Antworten:

391

-2147483648ist keine "Zahl". Die C ++ - Sprache unterstützt keine negativen Literalwerte.

-2147483648ist eigentlich ein Ausdruck: ein positiver Literalwert 2147483648mit einem unären -Operator davor. Der Wert 2147483648ist anscheinend zu groß für die positive Seite des intBereichs auf Ihrer Plattform. Wenn der Typ long intauf Ihrer Plattform eine größere Reichweite hätte, müsste der Compiler automatisch davon ausgehen, dass 2147483648der long intTyp vorhanden ist. (In C ++ 11 müsste der Compiler auch den long long intTyp berücksichtigen .) Dies würde den Compiler veranlassen, -2147483648in der Domäne eines größeren Typs auszuwerten, und das Ergebnis wäre erwartungsgemäß negativ.

In Ihrem Fall entspricht der Bereich anscheinend long intjedoch dem Bereich von int, und im Allgemeinen gibt es keinen ganzzahligen Typ mit einer größeren Reichweite als intauf Ihrer Plattform. Dies bedeutet formal, dass die positive Konstante 2147483648alle verfügbaren vorzeichenbehafteten Ganzzahltypen überläuft, was wiederum bedeutet, dass das Verhalten Ihres Programms undefiniert ist. (Es ist ein bisschen seltsam, dass sich die Sprachspezifikation in solchen Fällen für undefiniertes Verhalten entscheidet, anstatt eine Diagnosemeldung zu benötigen, aber so ist es.)

In der Praxis kann unter Berücksichtigung der Tatsache, dass das Verhalten nicht definiert ist, dies 2147483648als ein implementierungsabhängiger negativer Wert interpretiert werden, der zufällig positiv wird, nachdem er unär -angewendet wurde. Alternativ könnten einige Implementierungen versuchen, vorzeichenlose Typen zur Darstellung des Werts zu verwenden (z. B. mussten Compiler in C89 / 90 verwendet werden unsigned long int, jedoch nicht in C99 oder C ++). Implementierungen dürfen alles tun, da das Verhalten ohnehin undefiniert ist.

Als Randnotiz ist dies der Grund, warum Konstanten wie INT_MINtypischerweise definiert werden als

#define INT_MIN (-2147483647 - 1)

anstelle der scheinbar einfacheren

#define INT_MIN -2147483648

Letzteres würde nicht wie beabsichtigt funktionieren.

Ameise
quelle
78
Dies ist auch der Grund, warum dies getan wird : #define INT_MIN (-2147483647 - 1).
Orlp
5
@ RichardJ.RossIII - mit clang bekommst du wahrscheinlich ein 64-Bit-Literal, da es zu groß war, um in ein zu passen int. Die Implementierung von OP hat möglicherweise keinen 64-Bit-Typ.
Carl Norum
1
@ RichardJ.RossIII: Ich glaube, dieses Verhalten ist implementierungsdefiniert / undefiniert.
Oliver Charlesworth
3
Ich hätte nie gedacht, dass eine "negative Zahl" nicht als solche analysiert wird. Ich sehe keinen Grund. Ich hoffe, das -1.0wird als negativer Doppelwert analysiert, nicht wahr?
Leemes
6
@ qPCR4vir: Nein. Wie ich in meinem Kommentar zu Ihrer Antwort geschrieben habe, erlauben weder modernes C noch C ++ in diesem Fall die Verwendung von Typen ohne Vorzeichen (mit einer nicht angehängten Dezimalkonstante ). unsigned long intIn diesem Zusammenhang ist nur der erste Standard C (C89 / 90) zulässig , in C99 wurde diese Berechtigung jedoch entfernt. Nicht angehängte Literale in C und C ++ müssen signierte Typen haben. Wenn Sie hier einen nicht signierten Typ sehen, wenn ein signierter funktionieren würde, bedeutet dies, dass Ihr Compiler defekt ist. Wenn Sie hier einen vorzeichenlosen Typ sehen, bei dem kein vorzeichenbehafteter Typ funktionieren würde, ist dies nur eine spezifische Manifestation undefinierten Verhaltens.
Am
43

Der Compiler (VC2012) befördert auf die "minimalen" Ganzzahlen, die die Werte enthalten können. Im ersten Fall kann signed int(und long int) nicht (bevor das Zeichen angewendet wird), aber unsigned intkann: 2147483648hatunsigned int ???? Art. In der Sekunde zwingst du intaus dem unsigned.

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

Warnung C4146: Unärer Minusoperator auf Typ ohne Vorzeichen angewendet , Ergebnis immer noch ohne Vorzeichen

Hier sind verwandte "Kuriositäten":

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 Standard :

2.14.2 Ganzzahlige Literale [lex.icon]

Ein ganzzahliges Literal ist eine Folge von Ziffern ohne Punkt oder Exponententeil. Ein ganzzahliges Literal kann ein Präfix haben, das seine Basis angibt, und ein Suffix, das seinen Typ angibt.

Der Typ eines Integer-Literal ist der erste der entsprechenden Liste, in der sein Wert dargestellt werden kann.

Geben Sie hier die Bildbeschreibung ein

Wenn ein Integer-Literal nicht durch einen Typ in seiner Liste dargestellt werden kann und ein erweiterter Integer-Typ (3.9.1) seinen Wert darstellen kann, hat er möglicherweise diesen erweiterten Integer-Typ. Wenn alle Typen in der Liste für das Literal signiert sind, muss der erweiterte Integer-Typ signiert werden. Wenn alle Typen in der Liste für das Literal ohne Vorzeichen sind, muss der erweiterte Ganzzahltyp ohne Vorzeichen sein. Wenn die Liste sowohl vorzeichenbehaftete als auch vorzeichenlose Typen enthält, kann der erweiterte Ganzzahltyp signiert oder vorzeichenlos sein. Ein Programm ist fehlerhaft, wenn eine seiner Übersetzungseinheiten ein ganzzahliges Literal enthält, das von keinem der zulässigen Typen dargestellt werden kann.

Und dies sind die Beförderungsregeln für Ganzzahlen im Standard.

4.5 Integrale Werbeaktionen [conv.prom]

A prvalue eines ganzzahligen anderen Typ als bool, char16_t, char32_t, oder wchar_tderen ganzzahlige Umwandlungs rank (4,13) geringer ist als der Rang des int kann auf einen prvalue vom Typ umgewandelt werden , intwenn intalle Werte des Quelltyps darstellen; Andernfalls kann der Quellwert in einen Wert vom Typ konvertiert werden unsigned int.

qPCR4vir
quelle
3
@ qPCR4vir: In C89 / 90 die Compiler wurden Nutzungsarten soll int, long int, unsigned long intunsuffixed Dezimalkonstanten darzustellen. Dies war die einzige Sprache, in der vorzeichenlose Typen für nicht angehängte Dezimalkonstanten verwendet werden konnten. In C ++ 98 war es intoder long int. Keine vorzeichenlosen Typen erlaubt. Weder C (ab C99) noch C ++ erlauben dem Compiler, in diesem Zusammenhang vorzeichenlose Typen zu verwenden. Es steht Ihrem Compiler natürlich frei, nicht signierte Typen zu verwenden, wenn keiner der signierten funktioniert, aber dies ist immer noch nur eine spezifische Manifestation von undefiniertem Verhalten.
Am
@ AndrereyT. Toll! Natürlich deine Richtigkeit. Ist VC2012 kaputt?
qPCR4vir
@ qPCR4vir: AFAIK, VC2012 ist noch kein C ++ 11-Compiler (oder?), was bedeutet, dass er entweder intoder long intdarstellen muss 2147483648. Auch AFAIK, in VC2012 beide intund long intsind 32-Bit-Typen. Dies bedeutet, dass das Literal in VC2012 2147483648zu undefiniertem Verhalten führen sollte . Wenn das Verhalten undefiniert ist, darf der Compiler alles tun. Das würde bedeuten, dass VC2012 nicht kaputt ist. Es wurde lediglich eine irreführende Diagnosemeldung ausgegeben. Anstatt Ihnen zu sagen, dass das Verhalten völlig undefiniert ist, hat es sich entschieden, einen vorzeichenlosen Typ zu verwenden.
Am
@AndreyT: Wollen Sie damit sagen, dass Compiler frei sind, Nasendämonen zu emittieren, wenn der Quellcode ein nicht angefügtes Dezimalliteral enthält, das den Maximalwert eines signierten überschreitet long, und keine Diagnose ausstellen müssen? Das scheint kaputt zu sein.
Supercat
Gleiche "Warnung C4146" in VS2008 und "diese Dezimalkonstante ist nur in ISO C90 ohne Vorzeichen" in G ++
Spyder
6

Kurz 2147483648gesagt -2147483648, (-(-2147483648) > 0)läuft über und ist true.

Dies ist , wie 2147483648sieht binär mögen.

Außerdem ist bei vorzeichenbehafteten Binärberechnungen das höchstwertige Bit ("MSB") das Vorzeichenbit. Diese Frage kann erklären, warum.

drzymala
quelle
4

Da negation ( ) -2147483648tatsächlich darauf angewendet wird, entspricht die Zahl nicht Ihren Erwartungen. Es ist eigentlich das Äquivalent dieses Pseudocodes:2147483648-operator -(2147483648)

Angenommen, Ihr Compiler ist sizeof(int)gleich 4und CHAR_BITdefiniert als 8, würde der 2147483648Überlauf den maximal vorzeichenbehafteten Wert einer Ganzzahl ( 2147483647) ergeben. Was ist also das Maximum plus eins? Lassen Sie uns das mit einer 4-Bit-, 2s-Kompliment-Ganzzahl klären.

Warten! 8 läuft über die ganze Zahl! Was machen wir? Verwenden Sie die vorzeichenlose Darstellung 1000der Bits und interpretieren Sie sie als vorzeichenbehaftete Ganzzahl. Diese Darstellung lässt uns -8die 2s-Komplement-Negation anwenden 8, die, wie wir alle wissen, größer ist als 0.

Aus diesem Grund wird <limits.h>(und <climits>) üblicherweise INT_MINals ((-2147483647) - 1)- definiert, sodass die maximal vorzeichenbehaftete Ganzzahl ( 0x7FFFFFFF) negiert ( 0x80000001) und dann dekrementiert ( 0x80000000) wird.

Cole Johnson
quelle
Für eine 4-Bit-Zahl ist die Komplement-Negation der beiden -8immer noch -8.
Ben Voigt
Nur dass -8 als 0-8 interpretiert wird, nicht als negative 8. Und 8 läuft über ein 4-Bit-Int mit Vorzeichen
Cole Johnson
Überlegen Sie, -(8)was in C ++ dasselbe ist wie -8- es ist eine Negation, die auf ein Literal angewendet wird, nicht auf ein negatives Literal. Das Literal ist 8, das nicht in eine vorzeichenbehaftete 4-Bit-Ganzzahl passt, daher muss es ohne Vorzeichen sein. Das Muster ist 1000. Bisher ist Ihre Antwort richtig. Die Komplement-Negation der beiden 1000in 4 Bit ist 1000, es spielt keine Rolle, ob sie signiert oder nicht signiert ist. Ihre Antwort lautet "Interpretieren Sie die Bits als vorzeichenbehaftete Ganzzahl", wodurch der Wert -8nach der Komplement-Negation der beiden genau wie vor der Negation erstellt wird.
Ben Voigt
Natürlich gibt es in "4-Bit C ++" kein "Interpretieren der Bits als vorzeichenbehafteten ganzzahligen Schritt". Das Literal wird zum kleinsten Typ, der es ausdrücken kann. Dabei handelt es sich um eine vorzeichenlose 4-Bit-Ganzzahl . Der Wert des Literal ist 8. Negation wird angewendet (Modulo 16), was zu einer endgültigen Antwort von führt 8. Die Codierung ist immer noch 1000, aber der Wert ist unterschiedlich, da ein vorzeichenloser Typ ausgewählt wurde.
Ben Voigt