Этот код:
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
.
Согласно JLS раздел 15.15.4, -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
каким-то образом, надеясь, что мы никогда не позвоним Math.abs(long)
со значением, равным Long.MIN_VALUE
, так как у нас также есть Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.Мы можем использовать BigInteger
всюду, потому что 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.
Вот что говорит Java-документ для Math.abs() в javadoc:
Обратите внимание, что если аргумент равен значение Integer.MIN_VALUE, наибольшая отрицательная представимая величина int, результатом является то же значение, которое отрицательно.
Чтобы увидеть результат, который вы ожидаете, отбрасывайте Integer.MIN_VALUE
до long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
2147483648 не может быть сохранен в целое число в java, его двоичное представление такое же, как -2147483648.
Но (int) 2147483648L == -2147483648
Существует одно отрицательное число, которое не имеет положительного эквивалента, поэтому для него нет положительного значения. Вы увидите то же поведение с Long.MAX_VALUE.