2010-12-02 4 views
2

Je rencontre des problèmes avec l'arithmétique en virgule flottante qui n'est pas précise. J'essaie de calculer un score basé sur une formule pondérée où chaque variable d'entrée pèse environ 20 fois la suivante significative. Les entrées sont cependant des nombres réels, donc j'ai fini par utiliser un double pour stocker le résultat. Le code ci-dessous a le problème de perdre la différence entre E1 et E2.Comment contourner les problèmes d'arrondi en arithmétique à virgule flottante en C++?

Ce code est sensible aux performances, j'ai donc besoin de trouver une réponse efficace à ce problème. J'ai pensé à multiplier mes entrées par une centaine puis en utilisant un int (puisque cela serait assez précis je pense), mais je doute que ce soit la meilleure solution, d'où la question.

#include <iostream> 

int main() 
{ 
    double score1, score2; 
    float a = 2.75 ; 
    float b = 5.25 ; 
    float c = 5.25 ; 
    float d = 2.75 ; 
    float E1 = 3 ; 
    float E2 = 6 ; 

    score1 = 20 * b - 1 * a + 0.05 * d /* - 0.0025 * c*/ + 0.0001 * E1 ; 
    score2 = 20 * b - 1 * a + 0.05 * d /* - 0.0025 * c*/ + 0.0001 * E2 ; 

    std::cout << score1 << std::endl; 
    std::cout << score2 << std::endl; 

    std::cin.get(); 
    return 0; 
} 

//ouputs: 
//102.388 
//102.388 
+0

virgule flottante est limitée, à savoir qu'il ne peut pas représenter tous les nombres à virgule flottante, il y a beaucoup d'informations à ce sujet. – Drakosha

+1

Vous pourriez utiliser le double. Il y a une utilité limitée à se limiter à flotter (à part l'espace de stockage qui, sauf si vous êtes dans des situations très spécifiques, ne devrait pas poser de problème). –

Répondre

4
  1. vous n'êtes pas en sortie la totalité de la valeur, utilisez cout << setprecision(number_of_digits) << score1 << endl;
  2. combien de chiffres valides avez-vous besoin dans votre calcul de score?
+0

Merci, en fixant une plus grande précision, je vois les valeurs correctes maintenant, cela signifie-t-il que mon code fonctionne comme prévu quand je compare des scores comme 'if (score1 nus

+0

Eh bien, oui comparer deux flotteurs va comparer leur valeur. –

+0

Désolé si Im étant stupide, mais si vous voyez des valeurs erronées dans un journal de débogage, la première chose à supposer est qu'ils sont réellement faux, surtout si cela expliquerait le bug que vous recherchez. – nus

3

je pensais de multiplier mes entrées par cent puis en utilisant un int (car ce serait assez, je pense précis), mais je doute que ce soit la meilleure solution

Compte tenu de la les valeurs que vous avez montrées, je dirais que c'est.

+0

En réalité, multiplier les entrées par 100 ne fonctionnerait pas - tous les calculs n'aboutiraient pas à des entiers même si les entrées sont des entiers. Vous devez multiplier les entrées par 10000 ou 1000000 et modifier les constantes dans le calcul. –

1

http://ideone.com/qqTB3 vous montre que la différence ne soit pas perdu, mais en fait aussi grand que vous attendez (jusqu'à une précision en virgule flottante, qui est de 15 chiffres décimaux pour double).

+0

+ 1 pour me présenter à ideone.com – nus

1

permet de voir ce qui se passe dans ce code:

score1 = 20 * b - 1 * a + 0.05 * d /* - 0.0025 * c*/ + 0.0001 * E1 ; 

// Multiplication division happens first: 

float tmp1 = static_cast<float>(20) * b;  // 20 cast to float. 
float tmp2 = static_cast<float>(1) * a;  // 1 cast to float. 
double tmp3 = 0.05 * static_cast<double>(d); // d converted to double as 0.05 is double 
double tmp4 = 0.0001 * static_cast<double>(E1);// E1 cast to double as 0.0001 is double 

// Addition and subtraction now happen 
float tmp5 = tmp1 - tmp2; 
double tmp6 = static_cast<double>(tmp5) + tmp3; // tmp5 cast to double as tmp3 is a double. 
double tmp7 = tmp6 + tmp4; 
score1  = tmp7; 

Si nous faisons cela dans nos têtes:

tmp1 = 105.0 
tmp2 = 2.75 
tmp3 = 0.1375 
tmp4 = 0.0003 
tmp5 = 107.75 
tmp6 = 107.8875 
tmp7 = 107.8878 

La précision doit tenir pour ces valeurs:
Mais quand vous imprimez la précision par défaut pour les doubles est de 3 décimales.

std::cout << 107.8878; 
> 107.888 

donc la précision réglée:

std::cout << std::setprecision(15) << 107.8878 << "\n"; 
> 107.8878 
+0

Hmm, ça n'a pas l'air très performant ... Yat-il un meilleur moyen? – nus

+0

Que voulez-vous dire, il ne semble pas très perfomant (ce n'est pas un mot). C'est exactement comme cela que le compilateur va générer du code.Les variables temporaires seront probablement des registres et l'optimiseur est autorisé à permuter l'ordre des opérations (dans une certaine mesure), mais c'est exactement ce que fera le code sous-jacent. –

+0

Ce que j'essayais de montrer, c'est que la plupart de vos opérations se font en double. Donc, à moins que vous ayez des besoins d'espace très spécifiques, vos variables devraient probablement être doubles. –

Questions connexes