Pergunta

No Visual C ++, escrevi a seguinte amostra em um programa C ++:

float f1 = 42.48f;
double d1 = 42.48;
double d2 = f1;

Eu compilei o programa com o Visual Studio 2005. No depurador, vejo os seguintes valores:

f1  42.480000   float
d1  42.479999999999997  double
d2  42.479999542236328  double

D1 pelo meu conhecimento está ok, mas D2 está errado.

O problema ocorre também com /fp = preciso como com /fp = rigoroso como com /fp = rápido.

Qual é o problema aqui? Alguma dica de como evitar esse problema? Isso leva a sérios problemas numéricos.

Foi útil?

Solução

Este não é um problema com o VC ++ ou algo assim - é um problema fundamental de como os números de pontos flutuantes são armazenados no computador. Para mais informações, veja IEEE-754.

A questão é que uma conversão de flutuação para o dobro é feita de tal forma que a conversão de volta de dupla para flutuação resulta exatamente no mesmo valor de flutuação com o qual você começou. Não estou ciente de nenhuma maneira de contornar a perda de precisão, exceto para usar apenas duplas quando você precisar de uma precisão mais longa. Pode ser que tentar round O flutuador convertido em dois lugares decimais o definirá para o valor correto, mas não tenho certeza disso.

Outras dicas

O valor em f1 e o valor em d2 Ambos representam exatamente o mesmo número. Esse número não é exatamente 42.480000, nem é exatamente 42.479999542236328, embora tenha uma representação decimal que termine. Ao exibir carros alegóricos, sua visualização de depuração é sensivelmente arredondada com a precisão de um flutuador e, ao exibir duplas, está arredondando com a precisão de um duplo. Então você vê cerca de duas vezes mais figuras significativas do valor misterioso quando você converte e exibe como um dobro.

d1 contém uma aproximação melhor para 4,48 do que o valor misterioso, pois d1 contém o dobro mais próximo de 4.48, enquanto f1 e d2 contém apenas o valor mais próximo de flutuação para 4,48. O que você esperava d2 conter? F1 não consegue "lembrar" de que é "realmente deveria ser" 4.48, de modo que, quando se converte para o dobro, fica "mais preciso".

A maneira de evitá -lo depende de quais problemas numéricos sérios você quer dizer. Se o problema é que D1 e D2 não se comparam iguais, e você acha que eles deveriam, então a resposta é incluir uma pequena tolerância em suas comparações, por exemplo, substitua d1 == d2 com:

fabs(d1 - d2) <= (d2 * FLT_EPSILON)

Esse é apenas um exemplo, porém, não verifiquei se lida com este caso. Você precisa escolher uma tolerância que funcione para você e também pode ter que se preocupar com muitos casos de borda - o D2 pode ser zero, o valor pode ser infinito ou nan, possivelmente outros.

Se o problema é que D2 não é um valor suficientemente preciso para o seu algoritmo para produzir resultados precisos, então você deve evitar float valores e/ou use um algoritmo mais numericamente estável.

Não há nada de errado com o que está acontecendo aqui.

Devido à maneira como os números de ponto flutuante são representados na memória, 42.479999999999997 é a representação mais próxima de 42.48 que um duplo pode ter.

Leia este artigo:http://docs.sun.com/source/806-3568/ncg_goldberg.html

Isso explica o que está acontecendo lá. Infelizmente, não há nada que você possa fazer sobre o armazenamento dele.

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