Этот код:
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
считается выходящим за пределы допустимого диапазона.
Чтобы получить немного больше информации по этому вопросу, вы можете прочитать статью в Википедии о дополнении Two .
Указанное вами поведение действительно противоречит интуиции. Однако это поведение указано в 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 (проверьте это с помощью a 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
s в 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
?