2016-08-04 5 views
14

Ich habe einige seltsame Ergebnisse mit Integer-Division in C++. Ich versuche dies zu berechnen: -2147483648/-1.Wie funktioniert C++ Integer Division für Limit und negative Werte?

Was ich 3 unterschiedliche Ergebnisse in 3 verschiedenen Szenarien:

int foo(int numerator, int denominator) { 
    int res = numerator/denominator; // produces SIGFPE, Arithmetic exception interrupt 

    cout << res << endl; 
} 

int main() { 
    int res = -2147483648/-1; 
    cout << res << endl;    // prints -2147483648 
    cout << -2147483648/-1 << endl; // prints 2147483648 
    foo(-2147483648, -1); 
    return 0; 
} 

Warum hat die ganzzahligen Teilungsoperation unterschiedliche Ergebnisse in verschiedenen Situationen erzeugt?

+1

Erwähnenswert, dass Code nicht kompiliert auf Windows VS-2015 sagen 'negative Integralkonstante konvertiert in vorzeichenlosen Typ \t' und 'unary Minus-Operator auf unsigned Typ angewandt, Ergebnis noch vorzeichenlos' auf allen '-2147483648/-1' Zeilen –

+1

Einfache Antwort [hier] (http://stackoverflow.com/a/29355979/1460794). – wally

+2

Dies ist, wie Visual Studio es macht: '#define INT_MIN (-2147483647 - 1) // Minimum (signed) int Wert ' – wally

Antwort

12

Die wörtlichen -2147483648/-1 von Ihrem Compiler als 2147483648 in einem Datentyp berechnet wird, die breit genug ist, diesen Wert zu halten .

Wenn das Literal direkt ausgedruckt wird, wird der Wert korrekt ausgegeben. Wenn das Literal in res gespeichert ist, wird es in eine int umgewandelt. Ein int scheint auf Ihrem System 32 Bit breit zu sein. Der Wert 2147483648 kann nicht als 32-Bit-Ganzzahl mit Vorzeichen dargestellt werden, sodass die Umwandlung einen Überlauf verursacht. Dieser Überlauf führt auf Ihrem System zu dem Wert -2147483648 (wahrscheinlich verwendet es Zweierkomplement).

schließlich beim Versuch, die Teilung zur Laufzeit (in der foo-Funktion), die SIGFPE Ausnahme tritt an den Überlauf durch zuführen (da der int Datentyp kann das Ergebnis nicht darstellen).

Beachten Sie, dass alle diese drei Optionen basieren auf plattformabhängige Verhalten:

  • die Tatsache, dass der Compiler keine Fehler erzeugen (oder andere Ausgaben), wenn die wörtliche Berechnung überläuft und verwendet nur einen Datentyp groß genug, um das Ergebnis
  • die Tatsache, dass der Überlauf, wenn int Speicher die Literal zu halten erzeugt, die spezifische Wert (und keine andere Probleme)
  • die Tatsache, dass die SIGFPE Ausnahme ausgelöst wird, wenn während der Laufzeit überquell
+0

AFAICT-Integerüberlauf während der Kompilierung ist Undefined Behavior; Die Verwendung eines größeren Datentyps ist ein mögliches Verhalten, aber wie FirstSTep zeigt, besteht ein anderes Verhalten darin, Fehler zu vermeiden. – MSalters

+0

@MSalters: ja, ich sage so viel im letzten Absatz. –

+0

Was auch falsch ist: jede Plattform muss einen ausreichend breiten Datentyp unterstützen; 'long long int' ist sicherlich breit genug. – MSalters

12

Ihr Ergebnis könnte INT_MAX+1 sein, mit anderen Worten, es ist wahrscheinlich überläuft. Das ist Undefined Behavior und alles kann passieren. Zum Beispiel kann ein Compiler den Code direkt ablehnen.

(Ein System könnte INT_MAX >= 2147483648 haben, aber dann würden Sie das gleiche Ergebnis für die drei Testfälle erwarten)

10
int res = -2147483648/-1; 
cout << res << endl;    // prints -2147483648 
cout << -2147483648/-1 << endl; // prints 2147483648 
int res = numerator/denominator; // produces SIGFPE, Arithmetic exception interrupt 

Hinweis moment gibt es keine negativen integer literals.

Es gibt keine negativen Ganzzahlliterale. Ausdrücke wie -1 wenden den unären Minus-Operator auf den Wert an, der durch das Literal repräsentiert wird, was implizite Typumwandlungen beinhalten kann.

Die Literal 2147483648 größer ist als der Maximalwert von int, so sein Typ long sein (oder long long, hängt von Implementierung).Dann -2147483648 ist der Typ long, und das Ergebnis der Berechnung (-2147483648/-1) ist long auch.

Für den ersten Fall ist das Ergebnis 2147483648 vom Typ longimplicitly converted zu int ist, aber es ist größer als der Maximalwert von int, ist das Ergebnis der Implementierung definiert. (Es scheint, das Ergebnis wird umschlungen nach den Regeln der Darstellung (2-Komplement) hier, so erhalten Sie das Ergebnis -2147483648.)

Für den zweiten Fall ist das Ergebnis mit Typ long gedruckt direkt aus, so dass Sie Erhalte das richtige Ergebnis.

Für den dritten Fall, tun Sie die Berechnung auf zwei int s, und das Ergebnis kann nicht in dem Ergebnistyp (das heißt int) passen, signed integer arithmetic operation overflow passiert ist, ist das Verhalten nicht definiert ist. (Erzeugt SIGFPE, Arithmetische Ausnahme-Interrupt hier.)

+0

'-2147483648' könnte' lang lang' sein, z.B. auf MSVC (das hat 'LONG_MAX = 2147483647') – MSalters

+0

In älteren Versionen von C++ könnte es auch ein 32-Bit-Zeichen ohne Vorzeichen sein (was das Endergebnis nicht wirklich ändert, aber es ändert, wie Sie dorthin gelangen). – plugwash

+0

@plugwash Ich weiß nicht viel über "ältere" C++. :) Im Moment wird es 'long' oder' long long' (seit C++ 11), es wird nicht "unsigned" sein, es sei denn, Sie verwenden das Suffix 'u'. Die Darstellung ist implementationsdefiniert, so dass "long" durch eine 32-Bit-Ganzzahl dargestellt werden kann. – songyuanyao