Ten kod:
System.out.println(Math.abs(Integer.MIN_VALUE));
Zwroty -2147483648
Czy nie powinien zwracać wartości bezwzględnej jako 2147483648
?
java
absolute-value
user665319
źródło
źródło
Zachowanie, które wskazałeś, jest rzeczywiście sprzeczne z intuicją. Jednak to zachowanie jest określone przez javadoc dla
Math.abs(int)
: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)
wracaInteger.MIN_VALUE
. Zauważ też, że0x7FFFFFFF
jestInteger.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 dolong
wcześniej. My jednak musimyint
s, co nie działa, ponieważInteger.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
s z nadzieją, że nigdy nie zadzwonimyMath.abs(long)
z wartością równąLong.MIN_VALUE
, ponieważ też mamyMath.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 odInteger.MAX_VALUE
do0
zamiastInteger.MIN_VALUE
)Na koniec wydaje się, że problem ten jest znany od jakiegoś czasu. Zobacz na przykład ten wpis o odpowiedniej regule findbugs .
źródło
Oto co mówi dokument Java dla Math.abs () w javadoc :
źródło
Aby zobaczyć oczekiwany wynik, rzuć
Integer.MIN_VALUE
nalong
:System.out.println(Math.abs((long) Integer.MIN_VALUE));
źródło
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 programuMath.abs(long)
jako poprawki, kiedy pokazałeś go jako prosty sposób na „zobaczenie wyniku, którego oczekuje pytający”. Przepraszam.2147483648 nie może być przechowywany jako liczba całkowita w Javie, jego reprezentacja binarna jest taka sama jak -2147483648.
źródło
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.źródło
Jest to poprawka w Javie 15 będzie metodą int i long. Będą obecni na zajęciach
Metody.
public static int absExact(int a) public static long absExact(long a)
Jeśli zdasz
LUB
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.
źródło
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); }
źródło
s
tu jest ?Num
jest równeInteger.MIN_VALUE
przed fragmentem kodu?