dupla aritmética e igualdade em Java

Aqui está uma esquisitice (para mim, pelo menos). Essa rotina imprime true:

double x = 11.0; double y = 10.0; if (xy == 1.0) { // print true } else { // print false } 

Mas esta rotina imprime falso:

 double x = 1.1; double y = 1.0; if (xy == 0.1) { // print true } else { // print false } 

Alguém se importa em explicar o que está acontecendo aqui? Eu estou supondo que tem algo a ver com aritmética de inteiros para int s posando como float s. Além disso, existem outras bases (além de 10 ) que possuem essa propriedade?

1.0 tem uma representação binária exata. 0.1 não.

talvez você esteja perguntando por que 0.1 não é armazenado como uma mantissa de 1 e um expoente de -10? mas não é assim que funciona. não é um número decimal mais um expoente, mas um número binário. então “tempos 10” não é uma coisa natural.

desculpe, talvez a última parte não esteja clara. é melhor pensar no expoente como uma mudança de bits. nenhum deslocamento de bits converterá uma sequência infinita como 0,1 (decimal) em uma finita.

Editar
Eu sou corrigido por Andrew. Obrigado!

Java segue IEEE 754 com uma Base de 2, portanto não pode representar 0.1 corretamente (é aprox. 0.1000000000000000055511151231257827021181583404541015625 ou 1.1001100110011001100110011001100110011001100110011010 * 2^-4 no IEEE) que você pode descobrir baseado na representação binária do duplo como este (bit 63 = sinal, bits 62-52 = expoente e bits 51-0 sendo a mantissa):

 long l = Double.doubleToLongBits(0.1); System.out.println(Long.toBinaryString(l)); 

Eu apenas me deixei levar pelos resultados e pensei por um momento que os floats em Java estão trabalhando com uma Base de 10, caso em que teria sido possível representar 0.1 muito bem.

E agora, para limpar as coisas de uma vez por todas, aqui está o que acontece:

 BigDecimal bigDecimal1 = new BigDecimal(0.1d); BigDecimal bigDecimal2 = new BigDecimal(1.1d - 1.0); BigDecimal bigDecimal3 = new BigDecimal(1.1d); BigDecimal bigDecimal4 = new BigDecimal(1.0d); System.out.println(bigDecimal1.doubleValue()); System.out.println(bigDecimal2.doubleValue()); System.out.println(bigDecimal3.doubleValue()); System.out.println(bigDecimal4.doubleValue()); System.out.println(bigDecimal1); System.out.println(bigDecimal2); System.out.println(bigDecimal3); System.out.println(bigDecimal4); 

Saídas:

 0.1 0.10000000000000009 1.1 1.0 0.1000000000000000055511151231257827021181583404541015625 0.100000000000000088817841970012523233890533447265625 1.100000000000000088817841970012523233890533447265625 1 

Então o que acontece? 1,1-1,0 é equivalente a:
1.100000000000000088817841970012523233890533447265625 - 1 (Java não pode representar precisamente 1.1) que é 0.100000000000000088817841970012523233890533447265625 e isso é diferente do que o Java representa 0.1 internamente ( 0.1000000000000000055511151231257827021181583404541015625 )

Se você está se perguntando por que o resultado da subtração está sendo exibido como 0.10000000000000009 e o “0.1” é exibido como está, dê uma olhada aqui

Isso aparece em cálculos de moeda o tempo todo. Use BigDecimal se você precisar de representação numérica exata ao custo de não ter desempenho habilitado por hardware, é claro.

Intereting Posts