Visual C Float / conversão dupla errônea?
-
25-09-2019 - |
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.
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.