Este código:
System.out.println(Math.abs(Integer.MIN_VALUE));
Devoluciones -2147483648
¿No debería devolver el valor absoluto como 2147483648
?
Este código:
System.out.println(Math.abs(Integer.MIN_VALUE));
Devoluciones -2147483648
¿No debería devolver el valor absoluto como 2147483648
?
Respuestas:
Integer.MIN_VALUE
es -2147483648
, pero el valor más alto que puede contener un entero de 32 bits es +2147483647
. Intentar representar +2147483648
en un int de 32 bits efectivamente "pasará" a -2147483648
. Esto se debe a que, al usar enteros con signo, las representaciones binarias en complemento a dos de +2147483648
y -2147483648
son idénticas. Sin embargo, esto no es un problema, ya que +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 el complemento de dos .
El comportamiento que señala es, de hecho, contrario a la intuición. Sin embargo, este comportamiento es el especificado por el javadoc paraMath.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 Java:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
Es decir, en 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 a nivel de bits.
Para comprobar si esto suena bien, tomemos -1 como ejemplo.
El valor entero -1
se puede anotar como 0xFFFFFFFF
en hexadecimal en Java (verifique esto con uno println
o cualquier otro método). Tomando -(-1)
así da:
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Entonces funciona.
Probemos ahora con Integer.MIN_VALUE
. Sabiendo que el entero más bajo puede ser representado por 0x80000000
, es decir, el primer bit puesto a 1 y los 31 bits restantes puestos a 0, tenemos:
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
Y es por eso que Math.abs(Integer.MIN_VALUE)
vuelve 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 salir en punta por @Bombe , echar las int
s que long
antes. Nosotros, sin embargo, debemos
int
s, lo que no funciona porque
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
s de alguna manera esperando que nunca llamemos Math.abs(long)
con un valor igual a Long.MIN_VALUE
, ya que también lo hemos hecho Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.Podemos usar BigInteger
s en todas partes, porque de BigInteger.abs()
hecho siempre devuelve un valor positivo. Esta es una buena alternativa, aunque un poco más lenta que manipular tipos enteros sin procesar.
Podemos escribir nuestro propio contenedor para Math.abs(int)
, así:
/**
* 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
(esencialmente desbordando de Integer.MAX_VALUE
a en 0
lugar de Integer.MIN_VALUE
)Como nota final, este problema parece conocerse desde hace algún tiempo. Vea, por ejemplo, esta entrada sobre la regla correspondiente de findbugs .
Para ver el resultado que espera, envíe Integer.MIN_VALUE
a long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.abs
está siendo contradictorio al devolver un número negativo:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException
? Además, el comportamiento está claramente documentado en la documentación de la API.
Math.abs(long)
. Pido disculpas por mi error aquí: pensé que propusiste el uso de Math.abs(long)
como solución, cuando lo mostraste como una forma sencilla de "ver el resultado que espera el autor de la pregunta". Lo siento.
Pero (int) 2147483648L == -2147483648
hay un número negativo que no tiene equivalente positivo, por lo que no tiene un valor positivo. Verá el mismo comportamiento con Long.MAX_VALUE.
Hay una solución para esto en Java 15 será un método para int y long. Estarán presentes en las clases.
java.lang.Math and java.lang.StrictMath
Los métodos.
public static int absExact(int a)
public static long absExact(long a)
Si pasa
Integer.MIN_VALUE
O
Long.MIN_VALUE
Se lanza una excepción.
https://bugs.openjdk.java.net/browse/JDK-8241805
Me gustaría ver si se pasa Long.MIN_VALUE o Integer.MIN_VALUE, se devolvería un valor positivo y no una excepción, pero.
Math.abs no funciona todo el tiempo con números grandes. ¡Utilizo esta pequeña lógica de código que aprendí cuando tenía 7 años!
if(Num < 0){
Num = -(Num);
}
s
aquí?
Num
es igual Integer.MIN_VALUE
antes del fragmento?