Pergunta

O problema.

Compilador Microsoft Visual C ++ 2005, 32bits Windows XP SP3, CPU AMD 64 x2.

Código:

double a = 3015.0; 
double b = 0.00025298219406977296;
//*((unsigned __int64*)(&a)) == 0x40a78e0000000000  
//*((unsigned __int64*)(&b)) == 0x3f30945640000000  
double f = a/b;//3015/0.00025298219406977296;

O resultado do cálculo (ou seja, "F") é 11917835.000000000 (((não assinado __int64) (& f)) == 0x4166bb4160000000) Embora deva ser 11917834.814763514 (ou seja, ((não assinado __int64) (& f)) == 0x4166bb415a128aef).
Ou seja, a parte fracionária está perdida.
Infelizmente, preciso de uma parte fracionária para estar correta.

Perguntas:
1) Por que isso acontece?
2) Como posso resolver o problema?

Informação adicional:
0) O resultado é tomado diretamente Na janela "Watch" (não foi impressa e não esqueci de definir a precisão da impressão). Também forneci despejo hexadecimal da variável de ponto flutuante, por isso tenho certeza absoluta do resultado do cálculo.
1) A desmontagem de f = a/b é:

fld         qword ptr [a]  
fdiv        qword ptr [b]  
fstp        qword ptr [f]  

2) f = 3015/0,00025298219406977296; produz resultado correto (f == 11917834.814763514, ((não assinado __int64) (& f)) == 0x4166bb415a128aef), mas parece que, neste caso, o resultado é simplesmente calculado durante o tempo de compilação:

fld         qword ptr [__real@4166bb415a128aef (828EA0h)]  
fstp        qword ptr [f]  

Então, como posso resolver esse problema?

PS Encontrei uma solução alternativa temporária (preciso apenas de parte fracionária da divisão, então eu simplesmente uso f = fmod (a/b)/b no momento), mas ainda gostaria de saber como corrigir esse problema corretamente - A precisão dupla deve ser de 16 dígitos decimais, portanto, esse cálculo não deve causar problemas.

Foi útil?

Solução

Você está usando o DirectX em seu programa em qualquer lugar, pois isso faz com que a unidade de ponto flutuante seja alterada para o modo de precisão única, a menos que você diga especificamente quando você não cria o dispositivo e causaria exatamente isso

Outras dicas

Curiosamente, se você declarar A e B como flutuadores, obterá exatamente 11917835.000000000. Portanto, meu palpite é que há uma conversão para a precisão única acontecendo em algum lugar, seja na maneira como as constantes são interpretadas ou posteriormente nos cálculos.

Qualquer um dos casos é um pouco surpreendente, considerando o quão simples é o seu código. Você não está usando nenhuma diretiva do compilador exótico, forçando a precisão única para todos os números de ponto flutuante?

EDIT: Você realmente confirmou que o programa compilado gera um resultado incorreto? Caso contrário, o candidato mais provável para a conversão de precisão única (errônea) seria o depurador.

Se você precisar de matemática precisa, não use ponto flutuante.

Faça um favor a si mesmo e obtenha uma biblioteca Bignum com suporte racional de número.

Eu acho que você está imprimindo o número sem especificar uma precisão. Experimente isso:

#include <iostream>
#include <iomanip>

int main() { 
    double a = 3015.0; 
    double b = 0.00025298219406977296;
    double f = a/b;

    std::cout << std::fixed << std::setprecision(15) << f << std::endl;
    return 0;
}

Isso produz:

11917834.814763514000000

O que me parece correto. Estou usando o VC ++ 2008 em vez de 2005, mas acho que a diferença está no seu código, não no compilador.

Tem certeza de que está examinando o valor de f logo após a instrução FSTP? Se você tem otimizações ativadas, talvez a janela do relógio possa estar mostrando um valor assumido em algum ponto posterior (isso parece um pouco plausível ao dizer que está olhando para a parte fracionária de F mais tarde - algumas instruções acabam massando fora de alguma forma?)

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