Nun, was primitive Integer-Typen angeht, verarbeitet Java Over / Underflow überhaupt nicht (für Float und Double ist das Verhalten unterschiedlich, es wird auf +/- unendlich gespült, genau wie IEEE-754-Mandate).
Wenn Sie zwei Ints hinzufügen, erhalten Sie keine Anzeige, wenn ein Überlauf auftritt. Eine einfache Methode zur Überprüfung auf Überlauf besteht darin, den nächstgrößeren Typ zu verwenden, um die Operation tatsächlich auszuführen, und zu überprüfen, ob das Ergebnis für den Quelltyp noch im Bereich liegt:
public int addWithOverflowCheck(int a, int b) {
// the cast of a is required, to make the + work with long precision,
// if we just added (a + b) the addition would use int precision and
// the result would be cast to long afterwards!
long result = ((long) a) + b;
if (result > Integer.MAX_VALUE) {
throw new RuntimeException("Overflow occured");
} else if (result < Integer.MIN_VALUE) {
throw new RuntimeException("Underflow occured");
}
// at this point we can safely cast back to int, we checked before
// that the value will be withing int's limits
return (int) result;
}
Was Sie anstelle der Throw-Klauseln tun würden, hängt von Ihren Anwendungsanforderungen ab (Throw, Flush auf Min / Max oder einfach alles protokollieren). Wenn Sie bei langen Vorgängen einen Überlauf feststellen möchten, haben Sie mit Grundelementen kein Glück. Verwenden Sie stattdessen BigInteger.
Edit (2014-05-21): Da diese Frage ziemlich häufig angesprochen zu werden scheint und ich das gleiche Problem selbst lösen musste, ist es ziemlich einfach, den Überlaufzustand mit der gleichen Methode zu bewerten, mit der eine CPU ihr V-Flag berechnen würde.
Es ist im Grunde ein boolescher Ausdruck, der das Vorzeichen beider Operanden sowie das Ergebnis beinhaltet:
/**
* Add two int's with overflow detection (r = s + d)
*/
public static int add(final int s, final int d) throws ArithmeticException {
int r = s + d;
if (((s & d & ~r) | (~s & ~d & r)) < 0)
throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");
return r;
}
In Java ist es einfacher, den Ausdruck (im if) auf die gesamten 32 Bits anzuwenden und das Ergebnis mit <0 zu überprüfen (dies testet effektiv das Vorzeichenbit). Das Prinzip funktioniert für alle ganzzahligen primitiven Typen genau gleich. Wenn Sie alle Deklarationen in der obigen Methode in long ändern, funktioniert es lange.
Bei kleineren Typen muss aufgrund der impliziten Konvertierung in int (Einzelheiten finden Sie im JLS für bitweise Operationen) anstelle von <0 das Vorzeichenbit explizit maskiert werden (0x8000 für kurze Operanden, 0x80 für Byteoperanden, Casts anpassen und Parameterdeklaration entsprechend):
/**
* Subtract two short's with overflow detection (r = d - s)
*/
public static short sub(final short d, final short s) throws ArithmeticException {
int r = d - s;
if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
return (short) r;
}
(Beachten Sie, dass im obigen Beispiel der Ausdruck "Notwendigkeit zum Überziehen der Überlauferkennung" verwendet wird.)
Wie / warum funktionieren diese booleschen Ausdrücke? Erstens zeigt ein logisches Denken, dass ein Überlauf nur auftreten kann, wenn die Vorzeichen beider Argumente gleich sind. Denn wenn ein Argument negativ ist und eine positiv ist, ist das Ergebnis (Add) muss näher an Null ist , oder im Extremfall ein Argument Null ist , das gleiche wie das andere Argument. Da die Argumente selbst keine Überlaufbedingung erzeugen können, kann ihre Summe auch keinen Überlauf erzeugen.
Was passiert also, wenn beide Argumente das gleiche Vorzeichen haben? Schauen wir uns den Fall an, bei dem beide positiv sind: Wenn Sie zwei Argumente hinzufügen, die eine Summe erzeugen, die größer als die Typen MAX_VALUE ist, ergibt sich immer ein negativer Wert, sodass ein Überlauf auftritt, wenn arg1 + arg2> MAX_VALUE. Der maximale Wert, der sich ergeben könnte, wäre nun MAX_VALUE + MAX_VALUE (im Extremfall sind beide Argumente MAX_VALUE). Für ein Byte (Beispiel) würde dies 127 + 127 = 254 bedeuten. Wenn man die Bitdarstellungen aller Werte betrachtet, die sich aus der Addition von zwei positiven Werten ergeben können, stellt man fest, dass für diejenigen, die überlaufen (128 bis 254), Bit 7 gesetzt ist, während Bei allen, die nicht überlaufen (0 bis 127), ist Bit 7 (oberstes Vorzeichen) gelöscht. Genau das prüft der erste (rechte) Teil des Ausdrucks:
if (((s & d & ~r) | (~s & ~d & r)) < 0)
(~ s & ~ d & r) wird nur dann wahr, wenn beide Operanden (s, d) positiv und das Ergebnis (r) negativ sind (der Ausdruck funktioniert auf allen 32 Bits, aber das einzige Bit, an dem wir interessiert sind ist das oberste (Vorzeichen-) Bit, gegen das mit <0 geprüft wird.
Wenn nun beide Argumente negativ sind, kann ihre Summe niemals näher an Null liegen als eines der Argumente. Die Summe muss näher an minus unendlich sein. Der extremste Wert, den wir erzeugen können, ist MIN_VALUE + MIN_VALUE, was (wiederum für das Byte-Beispiel) zeigt, dass für jeden Wert im Bereich (-1 bis -128) das Vorzeichenbit gesetzt ist, während jeder mögliche Überlaufwert (-129 bis -256) ) hat das Vorzeichenbit gelöscht. Das Vorzeichen des Ergebnisses zeigt also erneut den Überlaufzustand. Das ist es, was die linke Hälfte (s & d & ~ r) auf den Fall prüft, in dem beide Argumente (s, d) negativ und ein positives Ergebnis sind. Die Logik entspricht weitgehend dem positiven Fall; Bei allen Bitmustern, die sich aus der Addition von zwei negativen Werten ergeben können, wird das Vorzeichenbit genau dann gelöscht, wenn ein Unterlauf aufgetreten ist.