93 votes

Comment dois-je faire la comparaison en virgule flottante?

Je suis en train d'écrire un peu de code où j'ai quelque chose le long des lignes de:

double a = SomeCalculation1();
double b = SomeCalculation2();

if (a < b)
    DoSomething2();
else if (a > b)
    DoSomething3();

Et puis dans d'autres endroits, j'ai peut-être besoin de faire de l'égalité:

double a = SomeCalculation3();
double b = SomeCalculation4();

if (a == 0.0)
   DoSomethingUseful(1 / a);
if (b == 0.0)
   return 0; // or something else here

En bref, j'ai beaucoup de calcul en virgule flottante en passe et j'ai besoin de faire des comparaisons pour les conditions. Je ne peux pas le convertir en entier mathématiques parce que une telle chose est dénuée de sens dans ce contexte.

J'ai lu avant que virgule flottante comparaisons peuvent être peu fiables, car vous pouvez avoir des choses comme ça:

double a = 1.0 / 3.0;
double b = a + a + a;
if (a != b)
    Console.WriteLine("Oh no!");

En bref, je voudrais savoir: Comment puis-je fiable comparer des nombres à virgule flottante (à moins que, plus que, de l'égalité)?

Le numéro de plage que j'utilise est à peu près de 10E-14 à 10E6, donc je n'ai besoin de travailler avec de petits nombres, ainsi que la grande.

J'ai balisé à ce que la langue agnostique parce que je suis intéressé à savoir comment je peux faire ce quelle que soit la langue que j'utilise.

75voto

Michael Borgwardt Points 181658

La comparaison pour le plus grand/plus petit n'est pas vraiment un problème, sauf si vous travaillez à droite au bord de l'float/double précision limite.

Pour un "flou est égal à" titre de comparaison, cette (code Java, devrait être facile à adapter) est ce que j'ai trouvé pour La virgule Flottante Guide , après beaucoup de travail et en tenant compte de beaucoup de critiques:

public static boolean nearlyEqual(float a, float b, float epsilon) {
    final float absA = Math.abs(a);
    final float absB = Math.abs(b);
    final float diff = Math.abs(a - b);

    if (a == b) { // shortcut, handles infinities
        return true;
    } else if (a == 0 || b == 0 || diff < Float.MIN_NORMAL) {
        // a or b is zero or both are extremely close to it
        // relative error is less meaningful here
        return diff < (epsilon * Float.MIN_NORMAL);
    } else { // use relative error
        return diff / (absA + absB) < epsilon;
    }
}

Il est livré avec une suite de tests. Vous devez immédiatement rejeter toute solution qui ne fonctionne pas, car il est pratiquement voué à l'échec dans certains cas limites comme ayant une valeur de 0, deux valeurs très faibles en face de zéro, ou infinis.

Une alternative (voir lien ci-dessus pour plus de détails) est de convertir les chars modèles de bits en entier et de tout accepter au sein d'un fixe entier distance.

En tout cas, il n'est probablement pas une solution qui est parfait pour toutes les applications. Idéalement, vous souhaitez développer/adapter votre propre avec une suite de tests couvrant votre des cas d'utilisation réels.

16voto

tech_loafer Points 91

// J'ai eu le problème de comparer les nombres à virgule flottante A <B et A> B // Voici ce qui semble fonctionner:

 if(A - B < Epsilon) && (fabs(A-B) > Epsilon)
{
    printf("A is less than B");
}

if (A - B > Epsilon) && (fabs(A-B) > Epsilon)
{
    printf("A is greater than B");
}
 

// Les fabs - valeur absolue - prennent en charge si elles sont essentiellement égales.

11voto

nni6 Points 719

Nous devons choisir un niveau de tolérance pour comparer les nombres flottants. Par exemple,

 final float TOLERANCE = 0.00001;
if (Math.abs(f1 - f2) < TOLERANCE)
    Console.WriteLine("Oh yes!");
 

Une note. Votre exemple est plutôt drôle.

 double a = 1.0 / 3.0;
double b = a + a + a;
if (a != b)
    Console.WriteLine("Oh no!");
 

Quelques maths ici
a = 1/3
b = 1/3 + 1/3 + 1/3 = 1.

1/3! = 1
Oh oui..

Tu veux dire

 if (b != 1)
    Console.WriteLine("Oh no!")
 

0voto

nelhage Points 1660

Le conseil standard consiste à utiliser une petite valeur "epsilon" (choisie en fonction de votre application, probablement), et à considérer les flottants se trouvant dans epsilon les mêmes. par exemple quelque chose comme

 #define EPSILON 0.00000001

if ((a - b) < EPSILON && (b - a) < EPSILON) {
  printf("a and b are about equal\n");
}
 

Une réponse plus complète est compliquée, car l'erreur en virgule flottante est extrêmement subtile et déroutante. Si vous vous souciez vraiment de l'égalité dans un sens précis, vous recherchez probablement une solution qui n'implique pas de virgule flottante.

-1voto

pnt Points 1314

Le meilleur moyen de comparer les doubles d'égalité / inégalité est de prendre la valeur absolue de leur différence et de la comparer à une valeur suffisamment petite (selon votre contexte).

 double eps = 0.000000001; //for instance

double a = someCalc1();
double b = someCalc2();

double diff = Math.abs(a - b);
if (diff < eps) {
    //equal
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X