Est-il sûr de vérifier les valeurs à virgule flottante pour l’égalité à 0?

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

Bien que je puisse comprendre les imprécisions entre 0.00000000000001 et 0.00000000000002, 0 lui-même semble assez difficile à gâcher car il ne s’agit que de rien. Si vous n’êtes pas précis sur rien, ce n’est plus rien.

Mais je ne sais pas grand chose sur ce sujet donc ce n’est pas à moi de le dire.

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

Est-ce que cela reviendra toujours vrai?

On peut 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 fragment de code original). Ceci est cohérent avec la sémantique de l’opérateur == . a == b signifie ” a est égal à b “.

Il n’est pas sûr (car ce n’est pas correct ) de s’attendre à ce que le résultat d’un calcul soit nul en arithmétique double (ou plus généralement en virgule flottante) chaque fois que le résultat du même calcul en mathématiques pures est nul. En effet, lorsque les calculs entrent en jeu, une erreur de précision en virgule flottante apparaît – un concept qui n’existe pas dans l’arithmétique des nombres réels en mathématiques.

Si vous avez besoin de faire beaucoup de comparaisons “d’égalité”, il pourrait être judicieux d’écrire une petite fonction d’assistance ou une méthode d’extension dans .NET 3.5 pour comparer:

 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); 

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

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

Rappelez-vous que .1 est un nombre décimal répétitif en binary et ne peut pas être représenté exactement. Ensuite, comparez cela à ce code:

 double d1 = 10.0 * .1; // make sure the comstackr 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 êtes plus susceptible de vous en souvenir.

De l’entrée MSDN pour Double.Equals :

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 de la précision différente des deux valeurs. L’exemple suivant indique que la valeur double .3333 et le double renvoyé en divisant 1 par 3 sont inégaux.

Plutôt que de comparer l’égalité, une technique recommandée consiste à définir une marge 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 aux différences de précision et, par conséquent, les valeurs risquent d’être égales. L’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é inégales.

Voir aussi Double.Epsilon .

Le problème survient lorsque vous comparez différents types d’implémentation de valeurs à virgule flottante, par exemple en comparant float à double. Mais avec le même type, cela ne devrait pas poser de 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 que la conversion de type implicite (double pour flotter) se produit pour la comparaison et qu’il en résulte un bogue.

Si le numéro est directement atsortingbué au flotteur ou au double, il est possible de le tester avec zéro ou tout nombre entier pouvant être représenté en 53 bits pour un double ou 24 bits pour un flottant.

Ou, pour le dire autrement, vous pouvez toujours atsortingbuer une valeur entière à un double, puis comparer le double au même nombre entier et être assuré qu’il sera égal.

Vous pouvez également commencer par atsortingbuer un nombre entier et effectuer des comparaisons simples en continuant à append, soustraire ou multiplier par des nombres entiers (en supposant que le résultat est inférieur à 24 bits pour un flottant et 53 bits pour un double). Vous pouvez donc traiter les flotteurs et les doubler comme des nombres entiers dans certaines conditions contrôlées.

Non, ce n’est pas correct. Les valeurs dites dénormalisées (sous-normales), comparées à 0.0, seraient comparées à des valeurs fausses (non nulles), mais si elles étaient utilisées dans une équation, elles seraient normalisées (deviendraient 0.0). Ainsi, utiliser ceci comme mécanisme pour éviter une division par zéro n’est pas sûr. Au lieu de cela, ajoutez 1.0 et comparez à 1.0. Cela garantira que toutes les sous-normales sont traitées comme zéro.

Essayez ceci et vous constaterez que == n’est pas fiable pour double / float.
double d = 0.1 + 0.2; bool b = d == 0.3;

Voici la réponse de Quora.

En fait, je pense qu’il est préférable d’utiliser les codes suivants pour comparer une valeur double à 0,0:

 double x = 0.0; return (Math.Abs(x) < double.Epsilon) ? true : false; 

Même chose pour float:

 float x = 0.0f; return (Math.Abs(x) < float.Epsilon) ? true : false;