Math.abs devuelve un valor incorrecto para Integer.Min_VALUE
-
12-11-2019 - |
Pregunta
Este código:
System.out.println(Math.abs(Integer.MIN_VALUE));
Devoluciones -2147483648
¿No debería devolver el valor absoluto como 2147483648
?
Solución
Integer.MIN_VALUE
es -2147483648
, pero el valor más alto que puede contener un entero de 32 bits es +2147483647
.Intentando representar +2147483648
en un int de 32 bits efectivamente se "pasará" a -2147483648
.Esto se debe a que, cuando se utilizan números enteros con signo, las representaciones binarias en complemento a dos de +2147483648
y -2147483648
Son identicos.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 a dos.
Otros consejos
El comportamiento que usted señala es, de hecho, contrario a la intuición.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.
Eso es, 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 el caso negativo, -x
.
De acuerdo con la JLS sección 15.15.4, el -x
es igual a (~x)+1
, dónde ~
es el operador de complemento bit a bit.
Para comprobar si esto suena bien, tomemos -1 como ejemplo.
El valor entero -1
Esto se puede notar como 0xFFFFFFFF
en hexadecimal en Java (mira esto con un println
o cualquier otro método).Tomando -(-1)
así da:
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Entonces funciona.
Intentemos ahora con Integer.MIN_VALUE
.Sabiendo que el número entero más bajo se puede representar 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 esto Math.abs(Integer.MIN_VALUE)
devoluciones 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 contrario a la intuición en el futuro?
Pudimos, como lo señala @Bombe, emitir nuestro
int
s along
antes.Nosotros, sin embargo, debemos- arrojarlos de nuevo a
int
s, lo cual no funciona porqueInteger.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
. - O continuar con
long
De alguna manera tengo la esperanza de que nunca llamemosMath.abs(long)
con un valor igual aLong.MIN_VALUE
, ya que también tenemosMath.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.
- arrojarlos de nuevo a
Nosotros podemos usar
BigInteger
está en todas partes, porqueBigInteger.abs()
De hecho, siempre devuelve un valor positivo.Esta es una buena alternativa, aunque un poco más lenta que manipular tipos enteros sin formato.Podemos escribir nuestro propio envoltorio para
Math.abs(int)
, como esto:
/**
* 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 bit a bit Y para borrar el bit alto, asegurándose de que el resultado no sea negativo:
int positive = value & Integer.MAX_VALUE
(esencialmente desbordante deInteger.MAX_VALUE
a0
en lugar deInteger.MIN_VALUE
)
Como nota final, este problema parece ser conocido desde hace algún tiempo.ver por ejemplo esta entrada sobre la regla findbugs correspondiente.
Aquí está lo que dice Java Doc para math.abs () en javadoc :
Tenga en cuenta que si el argumento es igual a el valor del entero.min_value, el El valor INT más representable negativo, El resultado es ese mismo valor, que es negativo.
Para ver el resultado que esperas, lanza Integer.MIN_VALUE
a long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
2147483648 no se puede almacenar en un entero en Java, su representación binaria es la misma que -2147483648.
pero (int) 2147483648L == -2147483648
Hay un número negativo que no tiene un equivalente positivo, por lo que no hay un valor positivo para ello.Verá el mismo comportamiento con Long.max_Value.
math.abs no funciona todo el tiempo con grandes números ¡Utilizo esta lógica de códigos que aprendí cuando tenía 7 años!
if(Num < 0){
Num = -(s);
}