Question

Le problème.

Microsoft Visual C ++ 2005 compilateur, Windows 32bit xp sp3, amd 64 cpu x2.

Code:

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

le résultat du calcul (c.-à "f") est 11.917.835,000000000 ( ((__int64 non signé ) (& f)) == 0x4166bb4160000000) bien qu'il devrait être 11.917.834,814763514 (ie ((__int64 non signé ) (& f)) == 0x4166bb415a128aef).
C'est à dire. partie décimale est perdue.
Malheureusement, je dois partie fractionnaire correcte.

Questions:
1) Pourquoi cela?
2) Comment puis-je résoudre le problème?

Informations complémentaires:
0) Le résultat est prise directement de la fenêtre « watch » (il n'a pas été imprimé, et je ne l'ai pas oublié de définir la précision d'impression). J'ai également fourni vidage hexadécimal de la variable à virgule flottante, donc je suis absolument sûr de résultat du calcul.
1) Le démontage de f = a / b est la suivante:

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

2) f = 3.015 / ,00025298219406977296; rendements résultat correct (f == 11.917.834,814763514, ((__int64 non signé ) (& f)) == 0x4166bb415a128aef), mais il semble que dans ce cas donner lieu est simplement calculé lors de la compilation:

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

Alors, comment puis-je résoudre ce problème?

P.S. J'ai trouvé une solution temporaire (j'ai besoin seulement une partie fractionnaire de la division, donc j'utilise simplement f = fmod (a / b) / b pour le moment), mais je voudrais encore savoir comment résoudre ce problème correctement - le double précision est censé être 16 chiffres décimaux, de sorte que ce calcul n'est pas censé causer des problèmes.

Était-ce utile?

La solution

Utilisez-vous directx dans votre programme partout comme qui cause l'unité de virgule flottante pour obtenir commuté en mode simple précision à moins que vous dire spécifiquement de ne pas lorsque vous créez l'appareil et vous faire exactement cela

Autres conseils

Il est intéressant, si vous déclarez a et b comme flotteurs, vous obtiendrez exactement 11.917.835,000000000. Donc, je suppose qu'il ya une conversion simple précision qui se passe quelque part, que ce soit dans la façon dont les constantes sont interprétées ou plus tard dans les calculs.

Les deux cas est un peu surprenant, mais, étant donné la simplicité de votre code est. Vous n'utilisez pas les directives du compilateur exotiques, forçant une précision unique pour tous les nombres à virgule flottante?

Edit: Avez-vous déjà confirmé que le programme compilé génère un résultat incorrect? Dans le cas contraire, le candidat le plus probable pour la (erronée) conversion de précision unique serait le débogueur.

Si vous avez besoin de mathématiques précis, ne pas utiliser à virgule flottante.

Faites-vous une faveur et obtenir une bibliothèque BigNum avec le soutien de nombre rationnel.

Je suppose que vous imprimez le numéro sans spécifier une précision. Essayez ceci:

#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;
}

produit:

11.917.834,814763514000000

Ce qui semble correct pour moi. J'utilise VC ++ 2008 au lieu de 2005, mais je suppose que la différence est dans votre code, pas le compilateur.

Êtes-vous sûr que vous examinez la valeur de droite f après l'instruction FSTP? Si vous avez des optimisations activées peut-être la fenêtre de la montre pourrait montrer une valeur prise à un moment plus tard (cela semble un peu plausible que vous dites que vous regardez la partie fractionnaire de f plus tard - fait un peu instruction vent en masquant en quelque sorte?)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top