Por que vejo uma variável dupla inicializado com algum valor como 21,4 como 21,399999618530273?

StackOverflow https://stackoverflow.com/questions/177506

  •  05-07-2019
  •  | 
  •  

Pergunta

double r = 11.631;
double theta = 21.4;

o depurador, estes são mostrados como 11.631000000000000 e 21.399999618530273.

Como posso evitar isso?

Foi útil?

Solução

problemas de precisão são devido ao representação interna de números de ponto flutuante e não há muito que você pode fazer para evitá-lo.

A propósito, imprimir esses valores em tempo de execução, muitas vezes ainda lidera aos resultados corretos, pelo menos, usando modernos compiladores C ++. Para a maioria das operações, isso não é um grande problema.

Outras dicas

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

Veja como há uma grande quantidade de 0110 0110 0110 lá no final? Isso porque 0,1 tem nenhuma representação exata em binário ... é um número binário de repetição. É uma espécie de como como 1/3 não tem representação em decimal. 1/3 é 0,33333333 e você tem que continuar a escrever 3 de sempre. Se você paciência perder, você tem algo inexata.

Então você pode imaginar como, em decimal, se você tentou fazer 3 * 1/3, e você não tem tempo para escrever 3 de sempre, o resultado que se obtém seria 0,99999999, não 1, e as pessoas ficar bravo 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 teta é realmente perto de 21,4, mas não necessariamente esse valor.

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

Isto é em parte específica da plataforma -. E não sabemos qual plataforma você está usando

É também em parte um caso de saber o que você realmente deseja para ver. O depurador está mostrando - até certo ponto, de qualquer maneira - o valor preciso armazenado na variável. Na minha artigo sobre números de ponto flutuante binários em .NET , há um C # classe que lhe permite ver o absolutamente exata número armazenado em um duplo. A versão on-line não está funcionando no momento. - Vou tentar colocar um em cima de outro site

Uma vez que o depurador vê o valor "real", ele tem que fazer um julgamento sobre o que mostrar - que poderia mostrar-lhe o valor arredondado para algumas casas decimais, ou um valor mais preciso. Alguns depuradores fazer um trabalho melhor do que outros em ler mentes dos desenvolvedores, mas é um problema fundamental com números de ponto flutuante binário.

Use o tipo decimal de ponto fixo se você quer estabilidade nos limites de precisão. Há despesas gerais, e você deve explicitamente convertidos se você deseja converter para ponto flutuante. Se você fizer convertido para ponto flutuante você vai reintroduzir as instabilidades que parecem incomodá-lo.

Como alternativa, você pode superar isso e aprender a trabalhar com a precisão limitada de aritmética de ponto flutuante. Por exemplo, você pode usar o arredondamento para tornar os valores convergem, ou você pode usar comparações epsilon para descrever uma tolerância. "Epsilon" é uma constante de configurar que define uma tolerância. Por exemplo, você pode optar por considerar dois valores como sendo iguais se eles estão dentro de 0,0001 uns dos outros.

Ocorre-me que você poderia usar a sobrecarga de operador para fazer comparações epsilon transparente. Isso seria muito legal.


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

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

Eu me deparei com isso antes ( em meu blog ) - Eu acho que a surpresa tende a ser que os números 'irracionais' são diferentes

.

Por 'irracional' aqui eu estou apenas referindo-se ao fato de que eles não podem ser representados com precisão neste formato. números irracionais reais (como p - pi). não pode ser representado com precisão em tudo

A maioria das pessoas está familiarizada com 1/3 não funciona no decimal: ,3333333333333 ...

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

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ê só tem que se acostumar com o fato de que alguns carros alegóricos são 'irracional', da mesma forma que 1/3 é.

Uma maneira você pode evitar isso é usar uma biblioteca que usa um método alternativo de representar números decimais, tais como BCD

Parece-me que 21,399999618530273 é a representação precisão simples (float) de 21,4. Parece que o depurador está lançando para baixo de dupla flutuar em algum lugar.

Se você estiver usando Java e você precisa de precisão, use a classe 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 limitado.

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

Se você precisa de precisão fixo, talvez você deve tentar números de ponto fixo. Ou mesmo inteiros. Eu, por exemplo, muitas vezes o uso int (1000 * x) para passar para o pager de depuração.

Se isso incomoda você, você pode personalizar a forma como alguns valores são exibidos durante a depuração. Use-o com cautela: -)

Enhancing Depuração com o Display Debugger Atributos

Consulte Geral Decimal Aritmética

Também tomamos nota quando se comparam floats, veja esta resposta para mais informações.

De acordo com o javadoc

"Se pelo menos um dos operandos a um operador numérico é do tipo double, então o
a operação é levada a cabo usando 64 bits aritmética de ponto flutuante, e o resultado da
operador numérico é um valor do tipo double. Se o outro operando não é uma dupla, é
primeiro arregalou (§5.1.5) para digitar duas vezes por promoção numérico (§5.6) ".

Aqui está a Fonte

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top