Este código:
System.out.println(Math.abs(Integer.MIN_VALUE));
Devoluções -2147483648
Não deveria retornar o valor absoluto como 2147483648
?
Este código:
System.out.println(Math.abs(Integer.MIN_VALUE));
Devoluções -2147483648
Não deveria retornar o valor absoluto como 2147483648
?
Respostas:
Integer.MIN_VALUE
é -2147483648
, mas o valor mais alto que um inteiro de 32 bits pode conter é +2147483647
. A tentativa de representar +2147483648
em um int de 32 bits efetivamente "rolará" para -2147483648
. Isso ocorre porque, ao usar inteiros com sinal, as representações binárias do complemento de dois de +2147483648
e -2147483648
são idênticas. Isso não é um problema, pois +2147483648
é considerado fora do intervalo.
Para ler um pouco mais sobre este assunto, você pode querer verificar o artigo da Wikipedia sobre o complemento de Dois .
O comportamento que você aponta é, de fato, contra-intuitivo. No entanto, esse comportamento é o especificado pelo javadoc paraMath.abs(int)
:
Se o argumento não for negativo, o argumento será retornado. Se o argumento for negativo, a negação do argumento é retornada.
Ou seja, Math.abs(int)
deve se comportar como o seguinte código Java:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
Ou seja, em caso negativo -x
,.
De acordo com a seção 15.15.4 do JLS , -x
é igual a (~x)+1
, onde ~
é o operador de complemento bit a bit.
Para verificar se isso está certo, vamos pegar -1 como exemplo.
O valor inteiro -1
pode ser anotado como 0xFFFFFFFF
em hexadecimal em Java (verifique isso com um println
ou qualquer outro método). Tomando -(-1)
assim dá:
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Então, funciona.
Vamos tentar agora com Integer.MIN_VALUE
. Sabendo que o menor inteiro pode ser representado por 0x80000000
, ou seja, o primeiro bit definido como 1 e os 31 bits restantes definidos como 0, temos:
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
E é por isso que Math.abs(Integer.MIN_VALUE)
retorna Integer.MIN_VALUE
. Observe também que 0x7FFFFFFF
é Integer.MAX_VALUE
.
Dito isso, como podemos evitar problemas devido a esse valor de retorno contra-intuitivo no futuro?
Poderíamos, como apontado por @Bombe , lançar nossos int
s para long
antes. Nós, no entanto, devemos
int
s, o que não funciona porque
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
s, de alguma forma, esperando que nunca paguemos Math.abs(long)
com um valor igual a Long.MIN_VALUE
, já que também fizemos Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.Podemos usar BigInteger
s em qualquer lugar, porque de BigInteger.abs()
fato sempre retorna um valor positivo. Esta é uma boa alternativa, embora um pouco mais lenta do que manipular tipos inteiros brutos.
Podemos escrever nosso próprio wrapper para Math.abs(int)
, assim:
/**
* 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
(essencialmente transbordando de Integer.MAX_VALUE
para em 0
vez de Integer.MIN_VALUE
)Como nota final, este problema parece ser conhecido há algum tempo. Veja, por exemplo, esta entrada sobre a regra de findbugs correspondente .
Para ver o resultado que você espera, lance Integer.MIN_VALUE
para long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.abs
está sendo contra-intuitivo ao retornar um número negativo:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException
? Além disso, o comportamento está claramente documentado na documentação da API.
Math.abs(long)
. Peço desculpas pelo meu erro aqui: achei que você propôs o uso de Math.abs(long)
como uma correção, quando o mostrou como uma forma simples de "ver o resultado que o autor da pergunta está esperando". Desculpe.
Mas (int) 2147483648L == -2147483648
há um número negativo que não possui equivalente positivo, portanto não há valor positivo para ele. Você verá o mesmo comportamento com Long.MAX_VALUE.
Existe uma correção para isso em Java 15 será um método para int e long. Eles estarão presentes nas aulas
java.lang.Math and java.lang.StrictMath
Os métodos.
public static int absExact(int a)
public static long absExact(long a)
Se você passar
Integer.MIN_VALUE
OU
Long.MIN_VALUE
Uma exceção é lançada.
https://bugs.openjdk.java.net/browse/JDK-8241805
Gostaria de ver se Long.MIN_VALUE ou Integer.MIN_VALUE é passado, um valor positivo seria return e não uma exceção, mas.
Math.abs não funciona o tempo todo com números grandes. Eu uso essa pequena lógica de código que aprendi quando tinha 7 anos!
if(Num < 0){
Num = -(Num);
}
s
aqui?
Num
igual Integer.MIN_VALUE
antes do snippet?