Math.abs возвращает неправильное значение для Integer.Min_VALUE - PullRequest
75 голосов
/ 26 марта 2011

Этот код:

System.out.println(Math.abs(Integer.MIN_VALUE));

Возвращает -2147483648

Разве это не должно возвращать абсолютное значение как 2147483648?

Ответы [ 6 ]

83 голосов
/ 26 марта 2011

Integer.MIN_VALUE равно -2147483648, но самое высокое значение, которое может содержать 32-разрядное целое число, равно +2147483647. Попытка представить +2147483648 в 32-битном int эффективно "перевернется" до -2147483648. Это связано с тем, что при использовании целых чисел со знаком двоичные представления двух дополнений +2147483648 и -2147483648 идентичны. Однако это не проблема, поскольку +2147483648 считается вне диапазона.

Чтобы немного больше прочитать по этому вопросу, вы можете проверить статью Википедии о дополнении Two .

32 голосов
/ 23 июня 2013

Поведение, на которое вы указываете, действительно противоречиво.Тем не менее, это поведение, указанное в 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 s к long раньше.Однако мы должны либо

    • привести их обратно к int с, что не работает, потому что 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 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 .

11 голосов
/ 26 марта 2011

Вот что говорит Java документ для Math.abs () в javadoc :

Обратите внимание, что если аргумент равен значение Integer.MIN_VALUE, самое отрицательное представимое значение int, результатом является то же значение, которое отрицательно.

2 голосов
/ 26 марта 2011

Чтобы увидеть ожидаемый результат, приведите Integer.MIN_VALUE к long:

System.out.println(Math.abs((long) Integer.MIN_VALUE));
0 голосов
/ 26 марта 2011

Но (int) 2147483648L == -2147483648 Существует одно отрицательное число, которое не имеет положительного эквивалента, поэтому для него нет положительного значения.Вы увидите то же поведение с Long.MAX_VALUE.

0 голосов
/ 26 марта 2011

2147483648 не может быть сохранено в целое число в Java, его двоичное представление такое же, как -2147483648.

...