Math.abs zwraca nieprawidłową wartość Integer.Min_VALUE


90

Ten kod:

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

Zwroty -2147483648

Czy nie powinien zwracać wartości bezwzględnej jako 2147483648?

Odpowiedzi:


102

Integer.MIN_VALUEjest -2147483648, ale najwyższa wartość, jaką może zawierać 32-bitowa liczba całkowita, to +2147483647. Próba przedstawienia +2147483648w 32-bitowej liczbie int spowoduje efektywne przeniesienie na -2147483648. Dzieje się tak, ponieważ w przypadku używania liczb całkowitych ze znakiem, binarne reprezentacje dopełnienia do dwóch +2147483648i -2147483648są identyczne. Nie stanowi to jednak problemu, ponieważ +2147483648jest uważane za poza zasięgiem.

Aby uzyskać więcej informacji na ten temat, możesz zapoznać się z artykułem w Wikipedii o uzupełnieniu do dwóch .


6
Cóż, nie jest problemem niedocenianie wpływu, może to oznaczać problemy. Osobiście wolałbym mieć wyjątek lub system liczbowy, który rozwija się dynamicznie w języku wyższego poziomu.
Maarten Bodewes

40

Zachowanie, które wskazałeś, jest rzeczywiście sprzeczne z intuicją. Jednak to zachowanie jest określone przez javadoc dlaMath.abs(int) :

Jeśli argument nie jest ujemny, zwracany jest argument. Jeśli argument jest ujemny, zwracana jest negacja argumentu.

Oznacza to, że Math.abs(int)powinien zachowywać się jak następujący kod Java:

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

Oznacza to, że w negatywnym przypadku -x.

Zgodnie z sekcją JLS 15.15.4 , -xjest równe (~x)+1, gdzie ~jest operatorem dopełniacza bitowego.

Aby sprawdzić, czy to brzmi dobrze, weźmy -1 jako przykład.

Wartość całkowitą -1można zapisać 0xFFFFFFFFw postaci szesnastkowej w Javie (sprawdź to za pomocą printlnlub innej metody). Biorąc w -(-1)ten sposób daje:

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

Więc to działa.

Spróbujmy teraz z Integer.MIN_VALUE. Wiedząc, że najniższą liczbę całkowitą można przedstawić za pomocą 0x80000000, czyli pierwszego bitu ustawionego na 1, a pozostałych 31 bitów ustawionych na 0, otrzymujemy:

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

I dlatego Math.abs(Integer.MIN_VALUE)wraca Integer.MIN_VALUE. Zauważ też, że 0x7FFFFFFFjest Integer.MAX_VALUE.

To powiedziawszy, w jaki sposób możemy uniknąć problemów z powodu tej sprzecznej z intuicją wartości zwracanej w przyszłości?

  • Moglibyśmy, jak podkreślił @Bombe , oddanych nasze ints do longwcześniej. My jednak musimy

    • wrzuć je z powrotem do ints, co nie działa, ponieważ Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE).
    • Lub kontynuuj longs z nadzieją, że nigdy nie zadzwonimy Math.abs(long)z wartością równą Long.MIN_VALUE, ponieważ też mamy Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE.
  • Możemy używać BigIntegers wszędzie, ponieważ BigInteger.abs()rzeczywiście zawsze zwraca wartość dodatnią. To dobra alternatywa, choć trochę wolniejsza niż manipulowanie surowymi typami liczb całkowitych.

  • Możemy napisać własne opakowanie Math.abs(int), na przykład:

/**
 * 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);
}
  • Użyj liczby całkowitej bitowej AND, aby wyczyścić wysoki bit, upewniając się, że wynik jest nieujemny: int positive = value & Integer.MAX_VALUE(zasadniczo przepełnia od Integer.MAX_VALUEdo 0zamiast Integer.MIN_VALUE)

Na koniec wydaje się, że problem ten jest znany od jakiegoś czasu. Zobacz na przykład ten wpis o odpowiedniej regule findbugs .


12

Oto co mówi dokument Java dla Math.abs () w javadoc :

Zwróć uwagę, że jeśli argument jest równy wartości Integer.MIN_VALUE, czyli najbardziej ujemnej wartości typu int, wynikiem jest ta sama wartość, która jest ujemna.


4

Aby zobaczyć oczekiwany wynik, rzuć Integer.MIN_VALUEna long:

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

1
Rzeczywiście możliwa poprawka! Nie rozwiązuje to jednak faktu, że Math.absjest to sprzeczne z intuicją, zwracając liczbę ujemną:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
bernard paulus

1
@bernardpaulus, cóż, co ma robić oprócz rzucania ArithmeticException? Ponadto zachowanie jest jasno udokumentowane w dokumentacji API.
Bombe

nie ma dobrej odpowiedzi na twoje pytanie ... Chciałem tylko zwrócić uwagę, że to zachowanie, które jest źródłem błędów, nie jest naprawiane przez użycie Math.abs(long). Przepraszam za mój błąd tutaj: myślałem, że zaproponowałeś użycie programu Math.abs(long)jako poprawki, kiedy pokazałeś go jako prosty sposób na „zobaczenie wyniku, którego oczekuje pytający”. Przepraszam.
bernard paulus

W Javie 15 z nowymi metodami w rzeczywistości generowany jest wyjątek.
chiperortiz

1

2147483648 nie może być przechowywany jako liczba całkowita w Javie, jego reprezentacja binarna jest taka sama jak -2147483648.


0

Ale (int) 2147483648L == -2147483648 jest jedna liczba ujemna, która nie ma dodatniego odpowiednika, więc nie ma dla niej wartości dodatniej. Zobaczysz to samo zachowanie w przypadku Long.MAX_VALUE.


0

Jest to poprawka w Javie 15 będzie metodą int i long. Będą obecni na zajęciach

java.lang.Math and java.lang.StrictMath

Metody.

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

Jeśli zdasz

Integer.MIN_VALUE

LUB

Long.MIN_VALUE

Zostaje zgłoszony wyjątek.

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

Chciałbym sprawdzić, czy zostanie przekazana wartość Long.MIN_VALUE lub Integer.MIN_VALUE, a zwrócona zostanie wartość dodatnia, a nie wyjątek, ale.


-1

Math.abs nie działa cały czas z dużymi liczbami. Używam tej małej logiki kodu, której nauczyłem się, gdy miałem 7 lat!

if(Num < 0){
  Num = -(Num);
} 

Co stu jest ?
aioobe

Przepraszam, że zapomniałem zaktualizować to z mojego oryginalnego kodu
Dave

Do czego to prowadzi, jeśli Numjest równe Integer.MIN_VALUEprzed fragmentem kodu?
aioobe
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.