java absolute-value

java - Math.abs devuelve un valor incorrecto para Integer.Min_VALUE



java math (6)

Este código:

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

Devuelve -2147483648

¿No debería devolver el valor absoluto como 2147483648 ?


2147483648 no se puede almacenar en un entero en Java, su representación binaria es la misma que -2147483648.


El comportamiento que señalas es, de hecho, contra-intuitivo. Sin embargo, este comportamiento es el especificado por el javadoc para Math.abs(int) :

Si el argumento no es negativo, se devuelve el argumento. Si el argumento es negativo, se devuelve la negación del argumento.

Es decir, Math.abs(int) debería comportarse como el siguiente código de Java:

public static int abs(int x){ if (x >= 0) { return x; } return -x; }

Es decir, en el caso negativo, -x .

De acuerdo con la sección 15.15.4 de JLS , -x es igual a (~x)+1 , donde ~ es el operador de complemento bit a bit.

Para comprobar si esto suena bien, tomemos -1 como ejemplo.

El valor entero -1 se puede notar como 0xFFFFFFFF en hexadecimal en Java (verifique esto con println o cualquier otro método). Tomar -(-1) da así:

-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1

Entonces, funciona

Permítanos probar ahora con Integer.MIN_VALUE . Sabiendo que el número entero más bajo se puede representar por 0x80000000 , es decir, el primer bit establecido en 1 y los 31 bits restantes en 0, tenemos:

-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1 = 0x80000000 = Integer.MIN_VALUE

Y esta es la razón Math.abs(Integer.MIN_VALUE) cual Math.abs(Integer.MIN_VALUE) devuelve Integer.MIN_VALUE . También tenga en cuenta que 0x7FFFFFFF es Integer.MAX_VALUE .

Dicho esto, ¿cómo podemos evitar problemas debido a este valor de retorno contraintuitivo en el futuro?

  • Podríamos, como lo señaló @Bombe , emitir nuestras int s long antes. Nosotros, sin embargo, debemos

    • devolverlos a int s, que no funciona porque Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE) .
    • O continúe con long s de alguna manera esperando que nunca llamemos Math.abs(long) con un valor igual a Long.MIN_VALUE , ya que también tenemos Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE .
  • Podemos utilizar BigInteger todas partes, porque BigInteger.abs() siempre devuelve un valor positivo. Esta es una buena alternativa, difícil un poco más lenta que la manipulación de tipos enteros sin procesar.

  • Podemos escribir nuestro propio contenedor para Math.abs(int) , como este:

/** * 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); }

  • Utilice un número entero Y para borrar el bit alto, asegurándose de que el resultado sea no negativo: int positive = value & Integer.MAX_VALUE (esencialmente desbordamiento de Integer.MAX_VALUE a 0 lugar de Integer.MIN_VALUE )

Como nota final, este problema parece conocerse desde hace tiempo. Vea, por ejemplo, esta entrada sobre la regla correspondiente de findbugs .


Esto es lo que Java doc dice para Math.abs () en javadoc :

Tenga en cuenta que si el argumento es igual al valor de Integer.MIN_VALUE, el valor int representable más negativo, el resultado es el mismo valor, que es negativo.


Para ver el resultado que esperas, Integer.MIN_VALUE en long :

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


Pero (int) 2147483648L == -2147483648 Hay un número negativo que no tiene un equivalente positivo, por lo que no tiene un valor positivo. Verá el mismo comportamiento con Long.MAX_VALUE.


Integer.MIN_VALUE es -2147483648 , pero el valor más alto que un entero de 32 bits puede contener es +2147483647 . Intentar representar +2147483648 en un int de 32 bits se "transferirá" de manera -2147483648 a -2147483648 . Esto se debe a que, cuando se utilizan enteros con signo, las representaciones binarias del complemento de dos de +2147483648 y -2147483648 son idénticas. Sin embargo, esto no es un problema, ya que el +2147483648 se considera fuera de rango.

Para leer un poco más sobre este tema, es posible que desee consultar el artículo de Wikipedia sobre complemento de Two .