Ten kod:
System.out.println(Math.abs(Integer.MIN_VALUE));
Zwroty -2147483648
Czy nie powinien zwracać wartości bezwzględnej jako 2147483648
?
Ten kod:
System.out.println(Math.abs(Integer.MIN_VALUE));
Zwroty -2147483648
Czy nie powinien zwracać wartości bezwzględnej jako 2147483648
?
Odpowiedzi:
Integer.MIN_VALUE
jest -2147483648
, ale najwyższa wartość, jaką może zawierać 32-bitowa liczba całkowita, to +2147483647
. Próba przedstawienia +2147483648
w 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 +2147483648
i -2147483648
są identyczne. Nie stanowi to jednak problemu, ponieważ +2147483648
jest 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 .
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 , -x
jest 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ą -1
można zapisać 0xFFFFFFFF
w postaci szesnastkowej w Javie (sprawdź to za pomocą println
lub 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 0x7FFFFFFF
jest 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 int
s do long
wcześniej. My jednak musimy
int
s, co nie działa, ponieważ
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
s 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ć BigInteger
s 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);
}
int positive = value & Integer.MAX_VALUE
(zasadniczo przepełnia od Integer.MAX_VALUE
do 0
zamiast 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 .
Aby zobaczyć oczekiwany wynik, rzuć Integer.MIN_VALUE
na long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.abs
jest to sprzeczne z intuicją, zwracając liczbę ujemną:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException
? Ponadto zachowanie jest jasno udokumentowane w dokumentacji API.
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.
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.
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.
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);
}
s
tu jest ?
Num
jest równe Integer.MIN_VALUE
przed fragmentem kodu?