web-dev-qa-db-fra.com

La bonne façon de comparer un System.Double à '0' (un nombre, int?)

Désolé, c'est peut-être une question facile et stupide, mais je dois savoir pour être sûr.

J'ai cette expression if,

void Foo()
{
    System.Double something = GetSomething();
    if (something == 0) //Comparison of floating point numbers with equality 
                     // operator. Possible loss of precision while rounding value
        {}
}

Est-ce que cette expression est égale à

void Foo()
{
    System.Double something = GetSomething();
    if (something < 1)
        {}
}

? Parce qu'alors je pourrais avoir un problème, entrer le if avec par exemple. une valeur de 0,9.

80
radbyx

Eh bien, à quelle distance avez-vous besoin que la valeur soit égale à 0? Si vous effectuez de nombreuses opérations en virgule flottante qui, avec une "précision infinie", peuvent donner 0, vous obtiendrez un résultat "très proche" de 0.

Généralement, dans cette situation, vous souhaitez fournir une sorte de epsilon et vérifier que le résultat est juste dans cette epsilon:

if (Math.Abs(something) < 0.001)

Epsilon que vous devriez utiliser est spécifique à l'application - cela dépend de ce que vous faites.

Bien sûr, si le résultat doit être exactement zéro, un simple contrôle d'égalité est acceptable.

100
Jon Skeet

Si something a été affecté à partir du résultat d'une opération autre que something = 0 alors vous feriez mieux d'utiliser:

if(Math.Abs(something) < Double.Epsilon)
{
//do something
}

Edit: Ce code est faux. Epsilon est le plus petit nombre, mais pas tout à fait zéro. Lorsque vous souhaitez comparer un nombre à un autre, vous devez déterminer quelle est la tolérance acceptable. Disons que rien au-delà de .00001 ne vous intéresse pas. C'est le nombre que vous utiliseriez. La valeur dépend du domaine. Cependant, ce n'est certainement certainement jamais Double.Epsilon.

29
sonatique

Votre something est un double et vous l'avez correctement identifié dans la ligne.

if (something == 0)

nous avons un double à gauche (lhs) et un int à droite (rhs).

Mais maintenant, il semble que vous pensiez que les lhs seront convertis en un int, puis le == signe comparera deux entiers. C'est pas ce qui se passe. La conversion de double en int est explicite et ne peut pas se produire "automatiquement".

Au contraire, le contraire se produit. Le rhs est converti en double, puis le == signe devient un test d'égalité entre deux doubles. Cette conversion est implicite (automatique).

Il est jugé préférable (par certains) d’écrire

if (something == 0.0)

ou

if (something == 0d)

car il est alors immédiat que vous compariez deux doubles. Cependant, c'est juste une question de style et de lisibilité, car le compilateur fera la même chose dans tous les cas.

Il est également pertinent, dans certains cas, d'introduire une "tolérance" comme dans la réponse de Jon Skeet, mais cette tolérance serait également un double. Ce pourrait bien sûr être 1.0 _ si vous vouliez, mais il n'est pas nécessaire que ce soit [le moins strictement positif] entier.

19

Si vous souhaitez simplement supprimer l'avertissement, procédez comme suit:

if (something.Equals(0.0))

Bien sûr, ce n’est une solution valable que si vous savez que la dérive n’est pas une préoccupation. Je fais souvent cela pour vérifier si je suis sur le point de diviser par zéro.

15
Russell Phillips

Je ne pense pas que ce soit égal, honnêtement. Pensez à votre propre exemple: quelque chose = 0,9 ou 0,0004. Dans le premier cas, ce sera FAUX, dans le deuxième cas, il sera VRAI. Traiter avec ces types, je définis généralement pour moi le pourcentage de précision et je compare dans cette précision. Cela dépend de vos besoins. quelque chose comme...

if(((int)(something*100)) == 0) {


//do something
}

J'espère que cela t'aides.

4
Tigran

Voici l'exemple présentant le problème (préparé dans LinQPad - si vous ne l'avez pas, utilisez simplement Console.Writeline au lieu de Dump méthode):

void Main()
{
    double x = 0.000001 / 0.1;
    double y = 0.001 * 0.01; 

    double res = (x-y);
    res.Dump();
    (res == 0).Dump();
}

Les deux x et y sont théoriquement identiques et égaux à: 0,00001 mais, en raison du manque de "précision infinie", ces valeurs sont légèrement différentes. Malheureusement, assez légèrement pour retourner false lorsque l'on compare à 0 de manière habituelle.

2
Michał Mądrzyk