Por que vejo uma variável dupla inicializada com algum valor como 21.4 como 21.399999618530273?

double r = 11.631; double theta = 21.4; 

No depurador, eles são mostrados como 11.631000000000000 e 21.399999618530273 .

Como posso evitar isso?

Esses problemas de precisão são devidos à representação interna de números de pontos flutuantes e não há muito o que fazer para evitá-los.

By the way, imprimir esses valores em tempo de execução, muitas vezes ainda leva aos resultados corretos, pelo menos, usando os modernos compiladores de C ++. Para a maioria das operações, isso não é um grande problema.

Gostei da explicação de Joel , que lida com um problema de precisão de ponto flutuante binário semelhante no Excel 2007:

Veja como há muito 0110 0110 0110 lá no final? Isso é porque 0.1 não tem representação exata em binário … é um número binário repetitivo. É uma espécie de como 1/3 não tem representação em decimal. 1/3 é 0.33333333 e você tem que continuar escrevendo 3’s para sempre. Se você perder a paciência, consegue algo impreciso.

Então você pode imaginar como, em decimal, se você tentasse fazer 3 * 1/3, e não tivesse tempo para escrever 3’s para sempre, o resultado seria 0.99999999, não 1, e as pessoas ficariam bravas com você por estar errado.

Se você tem um valor como:

 double theta = 21.4; 

E você quer fazer:

 if (theta == 21.4) { } 

Você tem que ser um pouco inteligente, você precisará verificar se o valor de theta está realmente próximo de 21.4, mas não necessariamente esse valor.

 if (fabs(theta - 21.4) <= 1e-6) { } 

Isso é parcialmente específico da plataforma – e não sabemos qual plataforma você está usando.

Também é em parte um caso de saber o que você realmente quer ver. O depurador está mostrando a você – até certo ponto, de qualquer forma – o valor exato armazenado em sua variável. No meu artigo sobre números binários de ponto flutuante no .net , há uma class c # que permite ver o número absolutamente exato armazenado em um duplo. A versão online não está funcionando no momento – vou tentar colocar uma em outro site.

Como o depurador vê o valor “real”, ele precisa fazer uma avaliação sobre o que exibir – ele pode mostrar o valor arredondado para algumas casas decimais ou um valor mais preciso. Alguns depuradores fazem um trabalho melhor que outros ao ler as mentes dos desenvolvedores, mas é um problema fundamental com números binários de ponto flutuante.

Use o tipo decimal ponto fixo se você quiser estabilidade nos limites de precisão. Existem overheads e você deve explicitamente converter se desejar converter em ponto flutuante. Se você converter em ponto flutuante, você reintroduzirá as instabilidades que parecem incomodá-lo.

Alternativamente, você pode superar isso e aprender a trabalhar com a precisão limitada da aritmética de ponto flutuante. Por exemplo, você pode usar o arredondamento para fazer convergir os valores ou pode usar comparações de epsilon para descrever uma tolerância. “Epsilon” é uma constante que você define que define uma tolerância. Por exemplo, você pode optar por considerar dois valores como iguais se estiverem dentro de 0,0001 um do outro.

Ocorre-me que você poderia usar a sobrecarga do operador para tornar as comparações de epsilon transparentes. Isso seria muito legal.


Para representações de expoente da mantissa, EPSILON deve ser calculado para permanecer dentro da precisão representável. Para um número N, Epsilon = N / 10E + 14

System.Double.Epsilon é o menor valor positivo representável para o tipo Double . É muito pequeno para o nosso propósito. Leia o conselho da Microsoft sobre o teste de igualdade

Eu já vi isso antes ( no meu blog ) – eu acho que a surpresa tende a ser que os números ‘irracionais’ são diferentes.

Por “irracional”, aqui estou apenas me referindo ao fato de que eles não podem ser representados com precisão neste formato. Números realmente irracionais (como π – pi) não podem ser representados com precisão.

A maioria das pessoas está familiarizada com 1/3 não trabalhando em decimal: 0.3333333333333 …

O curioso é que 1.1 não funciona em carros alegóricos. As pessoas esperam que os valores decimais funcionem em números de ponto flutuante por causa de como eles pensam neles:

1,1 é 11 x 10 ^ -1

Quando na verdade eles estão na base-2

1,1 é 154811237190861 x 2 ^ -47

Você não pode evitá-lo, você apenas tem que se acostumar com o fato de que alguns carros alegóricos são “irracionais”, da mesma forma que 1/3 é.

Uma maneira de evitar isso é usar uma biblioteca que use um método alternativo de representar números decimais, como BCD

Parece-me que 21.399999618530273 é a representação de precisão única (float) de 21.4. Parece que o depurador está sendo reduzido do dobro para flutuar em algum lugar.

Se você estiver usando Java e precisar de precisão, use a class BigDecimal para cálculos de ponto flutuante. É mais lento, mas mais seguro.

Você não pode evitar isso como você está usando números de ponto flutuante com quantidade fixa de bytes. Simplesmente não há isomorfismo possível entre números reais e sua notação limitada.

Mas na maioria das vezes você pode simplesmente ignorá-lo. 21.4 == 21.4 ainda seria verdade porque ainda são os mesmos números com o mesmo erro. Mas 21,4f == 21,4 pode não ser verdade porque o erro para float e double são diferentes.

Se você precisa de precisão fixa, talvez você deva tentar números de ponto fixo. Ou até inteiros. Por exemplo, eu uso frequentemente int (1000 * x) para passar para o debug pager.

Perigos da aritmética computacional

Se isso te incomoda, você pode personalizar a forma como alguns valores são exibidos durante a debugging. Use com cuidado 🙂

Aprimorando a debugging com os atributos de exibição do depurador

Consulte a aritmética decimal geral

Também tome nota ao comparar carros alegóricos, veja esta resposta para mais informações.

De acordo com o javadoc

“Se pelo menos um dos operandos para um operador numérico for do tipo double, então o
operação é realizada usando aritmética de ponto flutuante de 64 bits, e o resultado da
operador numérico é um valor do tipo double. Se o outro operando não for um duplo, é
primeiro ampliado (§5.1.5) para digitar em dobro por promoção numérica (§5.6). ”

Aqui está a fonte