105 votes

Est-il sûr de vérifier l'égalité des valeurs à virgule flottante avec 0 en C#/.NET ?

Je sais que vous ne pouvez pas compter sur l'égalité entre les valeurs de type double ou décimal normalement, mais je me demande si 0 est un cas spécial.

Si je peux comprendre les imprécisions entre 0,00000000000001 et 0,00000000000002, le 0 lui-même semble assez difficile à gâcher puisqu'il ne s'agit de rien. Si vous êtes imprécis sur rien, ce n'est plus rien.

Mais je ne connais pas grand-chose à ce sujet et ce n'est pas à moi de le dire.

double x = 0.0;
return (x == 0.0) ? true : false;

Cela reviendra-t-il toujours à la réalité ?

74 votes

T

7 votes

L

0 votes

I

122voto

Daniel Daranas Points 15123

Il est sûr pour s'attendre à ce que la comparaison retourne true si et seulement si la variable double a une valeur d'exactement 0.0 (ce qui est, bien sûr, le cas dans votre extrait de code original). Ceci est cohérent avec la sémantique de l'élément == opérateur. a == b signifie " a est égal à b ".

Il est pas sûr (parce que c'est pas correct ) de s'attendre à ce que le résultat d'un certain calcul soit nul en arithmétique double (ou, plus généralement, à virgule flottante) chaque fois que le résultat du même calcul en Mathématiques pures est nul. Cela est dû au fait que lorsque les calculs entrent dans le sol, l'erreur de précision en virgule flottante apparaît - un concept qui, inutile de le dire, n'existe pas dans l'arithmétique des nombres réels en Mathématiques.

53voto

0xA3 Points 73439

Si vous devez effectuer de nombreuses comparaisons d'"égalité", il peut être judicieux d'écrire une petite fonction d'aide ou une méthode d'extension dans .NET 3.5 pour la comparaison :

public static bool AlmostEquals(this double double1, double double2, double precision)
{
    return (Math.Abs(double1 - double2) <= precision);
}

Cela pourrait être utilisé de la manière suivante :

double d1 = 10.0 * .1;
bool equals = d1.AlmostEquals(0.0, 0.0000001);

4 votes

Y

1 votes

" m

15voto

Joel Coehoorn Points 190579

Pour votre échantillon simple, ce test est correct. Mais qu'en est-il de ceci :

bool b = ( 10.0 * .1 - 1.0 == 0.0 );

Rappelez-vous que .1 est une décimale répétée en binaire et qu'elle ne peut pas être représentée exactement. Comparez ensuite cela à ce code :

double d1 = 10.0 * .1; // make sure the compiler hasn't optimized the .1 issue away
bool b = ( d1 - 1.0 == 0.0 );

Je vous laisse faire un test pour voir les résultats réels : vous avez plus de chances de vous en souvenir ainsi.

5 votes

A

0 votes

W

0 votes

"

14voto

Stu Mackellar Points 8605

À partir de l'entrée MSDN pour Double.Equals :

La précision dans les comparaisons

La méthode Equals doit être utilisée avec prudence, car deux valeurs apparemment équivalentes peuvent être inégales en raison en raison de la différence de précision des deux valeurs. L'exemple suivant indique que la valeur Double .3333 et la valeur Double obtenue en divisant 1 par 3 sont inégales.

...

Plutôt que de comparer pour l'égalité, une technique recommandée consiste à de définir une marge acceptable de de différence acceptable entre deux valeurs (par exemple 0,01% de l'une des valeurs). Si la valeur absolue de la différence entre les deux valeurs est inférieure ou égale à cette marge, la différence est probablement due à des différences dans précision et, par conséquent, les valeurs sont probablement égales. L'exemple suivant exemple suivant utilise cette technique pour comparer .33333 et 1/3, les deux valeurs Double que l'exemple de code précédent a trouvé n'étaient pas égales.

Voir aussi Double.Epsilon .

1 votes

I x.Equals(y) , (1/x).Equals(1/y) , x est 0 y y est 1/Double.NegativeInfinity .

0 votes

@ x = 0 y y = 0 , 1/x != 1/y .

0 votes

@supercat : Ok, c'est l'une des choses que l'IEEE-754 se trompe. (Premièrement, le fait que 1.0/0.0 n'est pas NaN comme il devrait l'être, car la limite n'est pas unique. Deuxièmement, que les infinis se comparent les uns aux autres, sans prêter attention aux degrés d'infini)

6voto

Yogee Points 395

Le problème se pose lorsque vous comparez différents types d'implémentation de valeurs à virgule flottante, par exemple en comparant un flottant à un double. Mais avec le même type, cela ne devrait pas être un problème.

float f = 0.1F;
bool b1 = (f == 0.1); //returns false
bool b2 = (f == 0.1F); //returns true

Le problème est que le programmeur oublie parfois qu'une conversion implicite de type (double en flottant) a lieu pour la comparaison, ce qui entraîne un bug.

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