Цей код:
System.out.println(Math.abs(Integer.MIN_VALUE));
Повернення -2147483648
Чи не повинен він повертати абсолютне значення як 2147483648
?
Цей код:
System.out.println(Math.abs(Integer.MIN_VALUE));
Повернення -2147483648
Чи не повинен він повертати абсолютне значення як 2147483648
?
Відповіді:
Integer.MIN_VALUE
є -2147483648
, але найвищим значенням, яке може містити 32-бітове ціле число, є +2147483647
. Спроба представити +2147483648
в 32-бітному int ефективно "перекинеться" на -2147483648
. Це відбувається тому, що при використанні знакових цілих чисел, два доповнення до довічного уявлення +2147483648
і -2147483648
є ідентичними. Однак це не проблема, оскільки +2147483648
це вважається поза зоною дії.
Щоб трохи більше прочитати цього питання, ви можете переглянути статтю Вікіпедії про доповнення Двох .
Поведінка, на яку ви вказуєте, справді протиречувальна. Однак ця поведінка є такою, яку вказав javadoc дляMath.abs(int)
:
Якщо аргумент не негативний, аргумент повертається. Якщо аргумент негативний, повертається заперечення аргументу.
Тобто, Math.abs(int)
повинен поводитися як такий Java-код:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
Тобто, в негативному випадку -x
.
Відповідно до розділу 15.15.4 JLS , значення -x
дорівнює (~x)+1
, де ~
- побітове операторне доповнення.
Щоб перевірити, чи правильно це звучить, візьмемо -1 як приклад.
Ціле значення -1
може бути зазначене, як 0xFFFFFFFF
у шістнадцятковій в Java (перевірте це за допомогою println
будь-якого іншого методу). Прийом -(-1)
таким чином дає:
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Отже, це працює.
Давайте спробуємо зараз з Integer.MIN_VALUE
. Знаючи, що найнижче ціле число може бути представлене 0x80000000
, тобто першим бітом, встановленим у 1, і 31 бітом, що залишився, встановленим у 0, ми маємо:
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
І ось чому Math.abs(Integer.MIN_VALUE)
повертається Integer.MIN_VALUE
. Також зверніть увагу, що 0x7FFFFFFF
це Integer.MAX_VALUE
.
Тим не менш, як ми можемо уникнути проблем завдяки цій протиінтуїтивній віддачі у майбутньому?
Ми могли б, як зазначив @Bombe , відлитий наші int
з до long
раніше. Однак ми мусимо або те, і інше
int
s, що не працює, оскільки
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
s якось сподіваючись, що ми ніколи не зателефонуємо Math.abs(long)
зі значенням, рівним Long.MIN_VALUE
, оскільки ми також маємо Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.Ми можемо використовувати BigInteger
s скрізь, оскільки BigInteger.abs()
насправді завжди повертаємо позитивне значення. Це хороша альтернатива, хоча і трохи повільніша, ніж маніпулювання цілими цілими типами.
Ми можемо написати власну обгортку 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);
}
int positive = value & Integer.MAX_VALUE
( по суті , що перетікає з , Integer.MAX_VALUE
щоб 0
замість Integer.MIN_VALUE
)Наостанок, ця проблема, здається, відома вже деякий час. Див., Наприклад, цей запис про відповідне правило findbugs .
Щоб побачити результат, якого ви очікуєте, перейдіть Integer.MIN_VALUE
до long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.abs
є неінтуїтивним, повертаючи від'ємне число:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException
? Крім того, поведінка чітко задокументована в документації API.
Math.abs(long)
. Я вибачаюся за свою помилку: я вважав, що ви запропонували використовувати Math.abs(long)
як виправлення, коли ви показали це як простий спосіб "побачити результат, який очікує запитувач". Вибачте.
Але (int) 2147483648L == -2147483648
є одне негативне число, яке не має позитивного еквівалента, тому для нього немає позитивного значення. Ви побачите таку ж поведінку з Long.MAX_VALUE.
У Java 15 це виправлено, це буде метод int і long. Вони будуть присутні на заняттях
java.lang.Math and java.lang.StrictMath
Методи.
public static int absExact(int a)
public static long absExact(long a)
Якщо ви пройдете
Integer.MIN_VALUE
АБО
Long.MIN_VALUE
Наведено виняток.
https://bugs.openjdk.java.net/browse/JDK-8241805
Я хотів би побачити, чи передано Long.MIN_VALUE чи Integer.MIN_VALUE, позитивне значення буде поверненням, а не винятком, а.
Math.abs не працює постійно з великими цифрами. Я використовую цю маленьку логіку коду, яку я дізнався, коли мені було 7 років!
if(Num < 0){
Num = -(Num);
}
s
тут?
Num
дорівнює Integer.MIN_VALUE
до фрагмента?