web-dev-qa-db-fra.com

comparaison c ++ de deux valeurs doubles ne fonctionne pas correctement

Regardez ce code:

#include <cmath>
#include <iostream>
using namespace std;
class Sphere
{
    double r;
public:
    double V() const { return (4/3) * 3.14 * pow(r,3); }
    bool equal(const Sphere& s) const
    {
        cout  << V() << " == " << s.V() << " : " << ( V() == s.V() );
        return ( V() == s.V() );

    }

    explicit Sphere(double rr = 1): r(rr){}

};
main()
{
    Sphere s(3);
    s.equal(s);
}

La sortie est 84.78 == 84.78 : 0 Ce qui signifie que la même méthode ne renvoie pas la même valeur à chaque fois, même si tous les paramètres sont statiques?

Mais si j'écris 3.0 Au lieu de 3.14 Dans la définition de la méthode V(), comme ceci:

double V() const { return (4/3) * 3.0 * pow(r,3); }

Ensuite, la sortie est: 84.78 == 84.78 : 1

Qu'est-ce qui se passe ici? J'ai besoin de cette méthode, pour mon programme, qui va comparer les volumes de deux objets, mais c'est impossible? Je me suis cogné la tête pendant si longtemps pour comprendre quelle est la cause du problème et heureusement je l'ai trouvé, mais maintenant je ne comprends pas pourquoi ?? At-il quelque chose à voir avec le compilateur (GCC) ou est-ce que je manque quelque chose d'important ici?

15
tuks

Comparaison de valeurs à virgule flottante à l'aide de == L'opérateur est très sujet aux erreurs; deux valeurs qui devraient être égales peuvent ne pas être dues à des erreurs d'arrondi arithmétique. La façon la plus courante de les comparer est d'utiliser un epsilon:

bool double_equals(double a, double b, double epsilon = 0.001)
{
    return std::abs(a - b) < epsilon;
}
25
nijansen

Il existe deux problèmes avec les comparaisons à virgule flottante:

(1) Les opérations en virgule flottante impliquent généralement au moins de petites erreurs d'arrondi difficiles à prévoir. Par conséquent, deux opérations en virgule flottante qui devraient donner mathématiquement le même résultat (comme 4.7 * (1.0/3.14) vs 4.7/3.14) peuvent donner des résultats différents.

(2) Le compilateur est autorisé à effectuer des opérations en virgule flottante parfois avec une précision plus élevée que nécessaire. Il est également autorisé de faire exactement les mêmes opérations en virgule flottante avec juste la précision qui était nécessaire à d'autres moments. Par conséquent, la même opération exacte peut produire des résultats légèrement différents, ce que vous voyez ici.

Pour résoudre le problème de l'OP, cela semble provenir de (2). J'essaierais de trouver s'il existe des options de compilation qui peuvent empêcher le compilateur d'utiliser une précision plus élevée que nécessaire.

4
gnasher729