Ich habe ein einfaches Programm:
public class Mathz {
static int i = 1;
public static void main(String[] args) {
while (true){
i = i + i;
System.out.println(i);
}
}
}
Als ich dieses Programm ausführen, alles , was ich sehe , ist 0
für i
in meiner Ausgabe. Ich hätte das erste Mal erwartete Runde haben wir würden i = 1 + 1
, gefolgt von i = 2 + 2
, gefolgt von i = 4 + 4
usw.
Liegt das daran, dass i
der Wert auf den Wert zurückgesetzt wird, sobald wir versuchen, ihn auf der linken Seite erneut zu deklarieren 0
?
Wenn mich jemand auf die Einzelheiten hinweisen kann, wäre das großartig.
Ändern Sie das int
in long
und es scheint, als würden Zahlen wie erwartet gedruckt. Ich bin überrascht, wie schnell es den maximalen 32-Bit-Wert erreicht!
quelle
0
auf den ersten paar Iterationen, aber die Geschwindigkeit der Ausgabe wird diese Tatsache von den OP Verschleierung). Warum wird es akzeptiert?Einführung
Das Problem ist ein ganzzahliger Überlauf. Wenn es überläuft, kehrt es zum Minimalwert zurück und fährt von dort fort. Wenn es unterläuft, kehrt es zum Maximalwert zurück und fährt von dort fort. Das Bild unten zeigt einen Kilometerzähler. Ich benutze dies, um Überläufe zu erklären. Es ist ein mechanischer Überlauf, aber immer noch ein gutes Beispiel.
In einem Kilometerzähler, der
max digit = 9
über das Maximum hinausgeht9 + 1
, überträgt und gibt ein0
; Es gibt jedoch keine höhere Ziffer, die in a geändert werden kann1
, sodass der Zähler auf zurückgesetzt wirdzero
. Sie haben die Idee - "Integer Overflows" kommen jetzt in den Sinn.Somit
2147483647 + 1
läuft über und wickelt sich um-2147483648
. Daherint i=2147483647 + 1
wäre übergelaufen, was nicht gleich ist2147483648
. Außerdem sagen Sie "es wird immer 0 gedruckt". Dies ist nicht der Fall, da http://ideone.com/WHrQIW . Unten zeigen diese 8 Zahlen den Punkt, an dem es schwenkt und überläuft. Es beginnt dann, Nullen zu drucken. Seien Sie auch nicht überrascht, wie schnell es berechnet, die Maschinen von heute sind schnell.Warum ein ganzzahliger Überlauf "umhüllt"
Original PDF
quelle
Nein, es werden nicht nur Nullen gedruckt.
Wenn Sie dies ändern, werden Sie sehen, was passiert.
Was passiert, nennt man Überlauf.
quelle
true
miti<10000
:)while(k --> 0)
umgangssprachlich "whilek
goes to0
" verwenden können;)Ausgabe:
quelle
Da ich nicht genug Ruf habe, kann ich das Bild der Ausgabe für dasselbe Programm nicht in C mit kontrollierter Ausgabe veröffentlichen. Sie können selbst versuchen, zu sehen, dass es tatsächlich 32 Mal gedruckt wird, und dann, wie aufgrund des Überlaufs erläutert, i = 1073741824 + 1073741824 wechselt zu -2147483648 und eine weitere Addition liegt außerhalb des Bereichs von int und dreht sich zu Zero.
quelle
system("deltree C:")
Sie unter DOS / Windows arbeiten). Der vorzeichenbehaftete Ganzzahlüberlauf ist im Gegensatz zu Java in C / C ++ ein undefiniertes Verhalten. Seien Sie sehr vorsichtig, wenn Sie diese Art von Konstrukt verwenden.signed and unsigned
Ganzzahlen ohne undefiniertes Verhalteni += i
für mehr als 32 Iterationen ausgeführt wurdeif (i > 0)
. Der Compiler könnte dies optimieren,if(true)
da, wenn wir immer positive Zahlen hinzufügen,i
immer größer als 0 sein wird. Er könnte auch die Bedingung belassen, in der sie aufgrund des hier dargestellten Überlaufs nicht ausgeführt wird. Da der Compiler aus diesem Code zwei gleich gültige Programme erstellen kann, ist das Verhalten undefiniert.Der Wert von
i
wird mit einer festen Anzahl von Binärziffern im Speicher gespeichert. Wenn eine Nummer mehr Ziffern benötigt als verfügbar ist, werden nur die niedrigsten Ziffern gespeichert (die höchsten Ziffern gehen verloren).Das Hinzufügen
i
zu sich selbst ist dasselbe wie das Multipliziereni
mit zwei. Genau wie das Multiplizieren einer Zahl mit zehn in Dezimalschreibweise durch Verschieben jeder Ziffer nach links und Setzen einer Null nach rechts durchgeführt werden kann, kann das Multiplizieren einer Zahl mit zwei in Binärschreibweise auf die gleiche Weise durchgeführt werden. Dadurch wird rechts eine Ziffer hinzugefügt, sodass links eine Ziffer verloren geht.Hier ist der Startwert 1, wenn wir also 8 Ziffern zum Speichern verwenden
i
(zum Beispiel),00000001
00000010
00000100
und so weiter bis zum letzten Schritt ungleich Null
10000000
00000000
Unabhängig davon, wie viele Binärziffern zum Speichern der Nummer zugewiesen sind und wie hoch der Startwert ist, gehen schließlich alle Ziffern verloren, wenn sie nach links verschoben werden. Wenn Sie die Zahl nach diesem Zeitpunkt weiter verdoppeln, ändert sich die Zahl nicht mehr - sie wird weiterhin durch alle Nullen dargestellt.
quelle
Es ist korrekt, aber nach 31 Iterationen berechnet 1073741824 + 1073741824 nicht richtig und gibt danach nur noch 0 aus.
Sie können die Verwendung von BigInteger umgestalten, damit Ihre Endlosschleife ordnungsgemäß funktioniert.
quelle
int
.long
kann größere Zahlen darstellen alsint
kann.Zum Debuggen solcher Fälle empfiehlt es sich, die Anzahl der Iterationen in der Schleife zu reduzieren. Verwenden Sie dies anstelle von
while(true)
:Sie können dann sehen, dass es mit 2 beginnt und den Wert verdoppelt, bis es einen Überlauf verursacht.
quelle
Ich werde zur Veranschaulichung eine 8-Bit-Zahl verwenden, da diese in kurzer Zeit vollständig detailliert werden kann. Hex-Zahlen beginnen mit 0x, während Binärzahlen mit 0b beginnen.
Der Maximalwert für eine 8-Bit-Ganzzahl ohne Vorzeichen beträgt 255 (0xFF oder 0b11111111). Wenn Sie 1 hinzufügen, erwarten Sie normalerweise: 256 (0x100 oder 0b100000000). Aber da das zu viele Bits (9) sind, ist das über dem Maximum, so dass der erste Teil einfach gelöscht wird und Sie effektiv 0 haben (0x (1) 00 oder 0b (1) 00000000, aber mit der 1).
Wenn Ihr Programm ausgeführt wird, erhalten Sie:
quelle
Das größte Dezimalliteral des Typs
int
ist 2147483648 (= 2 31 ). Alle Dezimalliterale von 0 bis 2147483647 können überall dort erscheinen, wo ein int-Literal erscheinen kann, aber das Literal 2147483648 kann nur als Operand des unären Negationsoperators erscheinen -.Wenn eine ganzzahlige Addition überläuft, sind das Ergebnis die niederwertigen Bits der mathematischen Summe, wie sie in einem ausreichend großen Zweierkomplementformat dargestellt werden. Wenn ein Überlauf auftritt, stimmt das Vorzeichen des Ergebnisses nicht mit dem Vorzeichen der mathematischen Summe der beiden Operandenwerte überein.
quelle