Math.abs gibt einen falschen Wert für Integer.Min_VALUE zurück

90

Dieser Code:

System.out.println(Math.abs(Integer.MIN_VALUE));

Kehrt zurück -2147483648

Sollte es nicht den absoluten Wert als zurückgeben 2147483648?

user665319
quelle

Antworten:

98

Integer.MIN_VALUEist -2147483648, aber der höchste Wert, den eine 32-Bit-Ganzzahl enthalten kann, ist +2147483647. Der Versuch, +2147483648in einem 32-Bit-Int darzustellen, wird effektiv auf "rollen" -2147483648. Dies liegt daran, dass bei Verwendung vorzeichenbehafteter Ganzzahlen die beiden komplementären binären Darstellungen von +2147483648und -2147483648identisch sind. Dies ist jedoch kein Problem, da dies +2147483648als außerhalb des Bereichs liegend angesehen wird.

Wenn Sie mehr darüber lesen möchten, lesen Sie vielleicht den Wikipedia-Artikel über die Ergänzung von Two .

jonmorgan
quelle
6
Nun, kein Problem unterschätzt die Auswirkungen, es könnte durchaus Probleme bedeuten. Persönlich hätte ich lieber eine Ausnahme oder ein Zahlensystem, das in einer höheren Sprache dynamisch wächst.
Maarten Bodewes
38

Das Verhalten, auf das Sie hinweisen, ist in der Tat kontraintuitiv. Dieses Verhalten ist jedoch das vom Javadoc angegebene fürMath.abs(int) :

Wenn das Argument nicht negativ ist, wird das Argument zurückgegeben. Wenn das Argument negativ ist, wird die Negation des Arguments zurückgegeben.

Das heißt, Math.abs(int)sollte sich wie der folgende Java-Code verhalten:

public static int abs(int x){
    if (x >= 0) {
        return x;
    }
    return -x;
}

Das heißt, im negativen Fall -x.

Gemäß dem JLS-Abschnitt 15.15.4 ist das -xgleich (~x)+1, wobei ~der bitweise Komplementoperator ist.

Um zu überprüfen, ob dies richtig klingt, nehmen wir als Beispiel -1.

Der ganzzahlige Wert -1kann 0xFFFFFFFFin Java wie hexadezimal angegeben werden (überprüfen Sie dies mit einer printlnoder einer anderen Methode). Nehmen -(-1)gibt also:

-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1

Also funktioniert es.

Versuchen wir es jetzt mit Integer.MIN_VALUE. In dem Wissen, dass die niedrigste Ganzzahl durch 0x80000000das erste auf 1 gesetzte Bit und die 31 verbleibenden auf 0 gesetzten Bits dargestellt werden kann, haben wir:

-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1 
                     = 0x80000000 = Integer.MIN_VALUE

Und deshalb Math.abs(Integer.MIN_VALUE)kehrt zurück Integer.MIN_VALUE. Beachten Sie auch , dass 0x7FFFFFFFist Integer.MAX_VALUE.

Wie können wir jedoch in Zukunft Probleme aufgrund dieses kontraintuitiven Rückgabewerts vermeiden?

  • Wir könnten, wie von @Bombe betont , unsere ints longvorher besetzen . Wir müssen jedoch auch

    • wirf sie zurück in ints, was da nicht funktioniert Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE).
    • Oder fahren Sie mit longs fort und hoffen Sie irgendwie, dass wir niemals Math.abs(long)mit einem Wert gleich anrufen Long.MIN_VALUE, da wir auch haben Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE.
  • Wir können BigIntegers überall verwenden, weil BigInteger.abs()es in der Tat immer einen positiven Wert zurückgibt. Dies ist eine gute Alternative, wenn auch etwas langsamer als das Bearbeiten von rohen Ganzzahltypen.

  • Wir können unseren eigenen Wrapper für Folgendes schreiben Math.abs(int):

/**
 * Fail-fast wrapper for {@link Math#abs(int)}
 * @param x
 * @return the absolute value of x
 * @throws ArithmeticException when a negative value would have been returned by {@link Math#abs(int)}
 */
public static int abs(int x) throws ArithmeticException {
    if (x == Integer.MIN_VALUE) {
        // fail instead of returning Integer.MAX_VALUE
        // to prevent the occurrence of incorrect results in later computations
        throw new ArithmeticException("Math.abs(Integer.MIN_VALUE)");
    }
    return Math.abs(x);
}
  • Verwenden Sie ein ganzzahliges bitweises UND, um das hohe Bit zu löschen, und stellen Sie sicher, dass das Ergebnis nicht negativ ist: int positive = value & Integer.MAX_VALUE(im Wesentlichen überlaufend von Integer.MAX_VALUEnach 0statt statt Integer.MIN_VALUE)

Abschließend scheint dieses Problem seit einiger Zeit bekannt zu sein. Siehe zum Beispiel diesen Eintrag über die entsprechende Findbugs-Regel .

bernard paulus
quelle
11

Folgendes sagt das Java-Dokument für Math.abs () in javadoc :

Beachten Sie, dass wenn das Argument dem Wert von Integer.MIN_VALUE entspricht, dem negativsten darstellbaren int-Wert, das Ergebnis derselbe Wert ist, der negativ ist.

moe
quelle
4

Um das erwartete Ergebnis zu sehen, gehen Sie wie folgt Integer.MIN_VALUEvor long:

System.out.println(Math.abs((long) Integer.MIN_VALUE));
Bombe
quelle
1
Eine mögliche Lösung! Dies löst jedoch nicht die Tatsache, dass Math.abses nicht Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
intuitiv
1
@bernardpaulus, na ja, was soll es tun, außer ein zu werfen ArithmeticException? Das Verhalten ist auch in der API-Dokumentation klar dokumentiert.
Bombe
Es gibt keine gute Antwort auf Ihre Frage ... Ich wollte nur darauf hinweisen, dass dieses Verhalten, das eine Fehlerquelle darstellt, nicht durch die Verwendung von behoben wird Math.abs(long). Ich entschuldige mich für meinen Fehler hier: Ich habe gedacht, dass Sie die Verwendung Math.abs(long)als Fix vorgeschlagen haben, als Sie es als einfache Möglichkeit gezeigt haben, "das Ergebnis zu sehen, das der Fragesteller erwartet". Es tut uns leid.
Bernard Paulus
In Java 15 wird mit den neuen Methoden tatsächlich eine Ausnahme ausgelöst.
Chiperortiz
1

2147483648 kann in Java nicht in einer Ganzzahl gespeichert werden, seine binäre Darstellung entspricht der von -2147483648.

ymajoros
quelle
0

Es (int) 2147483648L == -2147483648 gibt jedoch eine negative Zahl, die kein positives Äquivalent hat, sodass es keinen positiven Wert dafür gibt. Sie werden das gleiche Verhalten mit Long.MAX_VALUE sehen.

Peter Lawrey
quelle
0

Es gibt eine Lösung dafür in Java 15 wird eine Methode zu int und long sein. Sie werden in den Klassen anwesend sein

java.lang.Math and java.lang.StrictMath

Die Methoden.

public static int absExact(int a)
public static long absExact(long a)

Wenn Sie bestehen

Integer.MIN_VALUE

ODER

Long.MIN_VALUE

Eine Ausnahme wird ausgelöst.

https://bugs.openjdk.java.net/browse/JDK-8241805

Ich würde gerne sehen, ob entweder Long.MIN_VALUE oder Integer.MIN_VALUE übergeben wird. Ein positiver Wert wäre return und keine Ausnahme, aber.

chiperortiz
quelle
-1

Math.abs funktioniert nicht immer mit großen Zahlen. Ich verwende diese kleine Codelogik, die ich mit 7 Jahren gelernt habe!

if(Num < 0){
  Num = -(Num);
} 
Dave
quelle
Was ist shier
Aioobe
Entschuldigung, ich habe vergessen, das von meinem ursprünglichen Code zu aktualisieren
Dave
Was bedeutet dieses Ergebnis , wenn Numequals Integer.MIN_VALUEvor dem Snippet?
Aioobe