web-dev-qa-db-fra.com

Comparer les valeurs doubles en C #

J'ai une variable double appelée x. Dans le code, x reçoit la valeur 0.1 et je la vérifie dans une instruction 'if' comparant x et 0.1

if (x==0.1)
{
----
}

Malheureusement, il n’entre pas la déclaration if

  1. Devrais-je utiliser Double ou double?

  2. Quelle est la raison derrière cela? Pouvez-vous suggérer une solution pour cela?

53

C'est un problème standard dû à la manière dont l'ordinateur stocke les valeurs en virgule flottante. Cherchez ici "problème de virgule flottante" et vous trouverez des tonnes d'informations.

En bref - un float/double ne peut pas stocker 0.1 avec précision. Ce sera toujours un peu éteint.

Vous pouvez essayer d'utiliser le type decimal qui stocke les nombres en notation décimale. Ainsi, 0.1 sera représentable avec précision.


Vous vouliez connaître la raison:

Les valeurs float/double sont stockées sous forme de fractions binaires et non de fractions décimales. Pour illustrer:

12.34 en notation décimale (ce que nous utilisons) signifie

2: 10.01 signifie 

1/3 en notation décimale est 0.3333333…. La même chose se passe en notation binaire, sauf que les nombres qui ne peuvent pas être représentés avec précision sont différents. Parmi eux se trouve le numéro 1/10. En notation binaire, c'est 0.000110011001100….

Comme la notation binaire ne peut pas la stocker avec précision, elle est stockée de manière arrondie. D'où votre problème.

82
Vilx-

double et Double sont identiques (double est un alias pour Double) et peut être utilisé indifféremment.

Le problème avec la comparaison d'un double avec une autre valeur est que les doubles sont des valeurs approximatives, et non des valeurs exactes. Ainsi, lorsque vous définissez x sur 0.1, il peut en réalité être stocké sous la forme 0.100000001 ou quelque chose du genre. 

Au lieu de vérifier l’égalité, vous devez vérifier que la différence est inférieure à une différence minimale définie (tolérance). Quelque chose comme: 

if (Math.Abs(x - 0.1) < 0.0000001)
{
    ...
}
34
Rune Grimstad

Vous avez besoin d'une combinaison de Math.Abs sur X-Y et d'un value pour pouvoir la comparer.

Vous pouvez utiliser la méthode de la méthode d'extension suivante

public static class DoubleExtensions
    {
        const double _3 = 0.001;
        const double _4 = 0.0001;
        const double _5 = 0.00001;
        const double _6 = 0.000001;
        const double _7 = 0.0000001;

        public static bool Equals3DigitPrecision(this double left, double right)
        {
            return Math.Abs(left - right) < _3;
        }

        public static bool Equals4DigitPrecision(this double left, double right)
        {
            return Math.Abs(left - right) < _4;
        }

        ...

Comme vous appelez rarement des méthodes sur double sauf ToString je crois que c'est une extension relativement sûre.

Ensuite, vous pouvez comparer x et y comme

if(x.Equals4DigitPrecision(y))

19
Valentin Kuzub

La comparaison des nombres en virgule flottante ne peut pas toujours être effectuée précisément à cause de l'arrondissement. Comparer

(x == .1)

l'ordinateur compare vraiment

(x - .1) vs 0

Le résultat de la sybtraction ne peut pas toujours être représenté précisément à cause de la façon dont le nombre à virgule flottante est représenté sur la machine. Par conséquent, vous obtenez une valeur différente de zéro et la condition est évaluée à false.

Pour surmonter cette comparaison

Math.Abs(x- .1) vs some very small threshold ( like 1E-9)
10
sharptooth

De la documentation

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és 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 (telle que 0,01% d’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 de précision et, par conséquent, les valeurs sont probablement é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ées inégal.

Donc si vous avez vraiment besoin d'un double, vous devriez utiliser la technique décrite dans la documentation ..__ Si possible, changez-le en décimal. Ce sera plus lent, mais vous n'aurez pas ce type de problème.

5
Alfred Myers

Double et double sont identiques.

Pour la raison, voir http://www.yoda.arachsys.com/csharp/floatingpoint.html . En bref: un double n'est pas un type exact et une différence minute entre "x" et " 0,1 "va le jeter.

3
Hans Kesting

On sait que la comparaison exacte des valeurs en virgule flottante ne fonctionne pas toujours en raison des problèmes d’arrondi et de représentation interne.

Essayez une comparaison imprécise:

if (x >= 0.099 && x <= 0.101)
{
}

L'autre alternative consiste à utiliser le type de données décimal.

3
user151323

Utilisez decimal. Il n'a pas ce "problème". 

2
Svetlozar Angelov

En vous appuyant sur la base de code Java, essayez d'utiliser .CompareTo et testez la comparaison à zéro. Cela suppose que la fonction .CompareTo prend en compte l'égalité de virgule flottante de manière précise. Par exemple,

System.Math.PI.CompareTo(System.Math.PI) == 0

Ce prédicat doit renvoyer true.

2
Nicole Tedesco

1) Devrais-je utiliser double ou double ???

Double et double est la même chose. double est juste un mot-clé C # fonctionnant comme un alias pour la classe System.Double La chose la plus courante est d'utiliser les alias! Idem pour string (System.String), int (System.Int32)

Voir aussi Tableau des types intégrés (référence C #)

1
Lars Corneliussen

Double (appelé float dans certaines langues) est une source de problèmes liés aux problèmes d'arrondis. Il n'est utile que si vous avez besoin de valeurs approximatives.

Le type de données décimal fait ce que vous voulez.

Pour référence, les valeurs décimales et décimales sont identiques dans .NET C #, de même que les types double et double, ils font tous deux référence au même type (décimal et double sont très différents, comme vous l'avez vu).

Notez que le type de données Decimal comporte des coûts, utilisez-le avec prudence si vous recherchez des boucles, etc.

1
Timothy Walters
// number of digits to be compared    
public int n = 12 
// n+1 because b/a tends to 1 with n leading digits
public double Epsilon { get; } = Math.Pow(10, -(n+1)); 

public bool IsEqual(double a, double b)
{
    if (Math.Abs(a)<= double.Epsilon || Math.Abs(b) <= double.Epsilon)
        return Math.Abs(a - b) <=  double.Epsilon;
    return Math.Abs(1.0 - a / b) <=  Epsilon;
}
0
Sorush

En règle générale:

La double représentation suffit dans la plupart des cas mais peut échouer lamentablement dans certaines situations. Utilisez des valeurs décimales si vous avez besoin d'une précision complète (comme dans les applications financières).

La plupart des problèmes avec les doubles ne proviennent pas de la comparaison directe, mais résultent généralement de l’accumulation de plusieurs opérations mathématiques qui perturbent de manière exponentielle la valeur en raison d’arrondis et d’erreurs fractionnaires (en particulier avec des multiplications et des divisions).

Vérifiez votre logique, si le code est:

x = 0.1

if (x == 0.1)

cela ne devrait pas échouer, c'est trop simple pour échouer, si la valeur X est calculée par des moyens ou des opérations plus complexes, il est fort possible que la méthode ToString utilisée par le débogueur utilise un arrondi intelligent, vous pouvez peut-être faire de même (si c'est trop risqué retour à la décimale):

if (x.ToString() == "0.1")
0
Jorge Córdoba

les représentations de nombres en virgule flottante sont notoirement inexactes (en raison de la manière dont les flottants sont stockés en interne), par ex. x peut en réalité être 0.0999999999 ou 0.100000001 et votre condition échouera. Si vous voulez déterminer si les flottants sont égaux, vous devez préciser s'ils sont égaux à une certaine tolérance.

c'est à dire. 

if(Math.Abs(x) - 0.1 < tol) {
   // Do something 
}
0
Massif