web-dev-qa-db-fra.com

Double.Epsilon pour l'égalité, supérieur, inférieur, inférieur ou égal à, supérieur ou égal à

http://msdn.Microsoft.com/en-us/library/system.double.epsilon.aspx

Si vous créez un algorithme personnalisé, cela détermine si deux virgule flottante les nombres peuvent être considérés comme égaux, vous doit utiliser une valeur supérieure à la constante d'Epsilon pour établir le marge absolue acceptable de différence pour que les deux valeurs soient considéré comme égal. (En règle générale, cette marge de différence est plusieurs fois supérieure à Epsilon.)

Donc, n’est-ce pas vraiment un epsilon qui pourrait être utilisé pour des comparaisons? Je ne comprends pas vraiment le libellé MSDN.

Peut-il être utilisé comme epsilon dans les exemples ici? - Quel est le moyen le plus efficace pour les comparaisons flottantes et doubles?

Et finalement, cela semble vraiment important, je voudrais donc avoir une implémentation solide pour l’égalité, supérieure, inférieure, inférieure ou égale et supérieure ou égale à.

50
ss2k

Je ne sais pas quoi ils fumaient quand ils ont écrit ça. Double.Epsilon est la plus petite valeur à virgule flottante non dénormale représentable qui ne soit pas 0. Tout ce que vous savez, c'est que, s'il y a une erreur de troncature, elle sera toujours plus grande que cette valeur. Beaucoup plus gros.

Le type System.Double peut représenter des valeurs précises jusqu'à 15 chiffres. Donc, une simple estimation de premier ordre si une valeur double x est égale à une constante consiste à utiliser un epsilon de constante * 1E-15

public static bool AboutEqual(double x, double y) {
    double epsilon = Math.Max(Math.Abs(x), Math.Abs(y)) * 1E-15;
    return Math.Abs(x - y) <= epsilon;
}

Vous devez cependant faire attention aux erreurs de troncature. Si x et y sont des valeurs calculées, vous devez augmenter le nombre d'epsilon.

74
Hans Passant

Je voudrais m'assurer que j'ai une implémentation solide pour l'égalité, supérieure, inférieure, inférieure ou égale à, et supérieure ou égale à.

Vous utilisez l'arithmétique binaire en virgule flottante.

L'arithmétique binaire en virgule flottante a été conçue pour représenter des quantités physiques telles que la longueur, la masse, la charge, le temps, etc.

Vous utilisez probablement l'arithmétique binaire en virgule flottante telle qu'elle était censée être utilisée: pour effectuer l'arithmétique sur des quantités physiques.

Les mesures de grandeurs physiques ont toujours une précision particulière en fonction de la précision du dispositif utilisé pour les mesurer.

Puisque vous êtes celui qui fournit les valeurs pour les quantités que vous manipulez, vous êtes celui qui sait quelles sont les "barres d'erreur" sur cette quantité. Par exemple, si vous fournissez la quantité "la hauteur du bâtiment est de 123,56 mètres", vous savez alors que cela correspond au centimètre près, mais pas au micromètre.

Par conséquent, lorsque l'on compare deux quantités pour déterminer leur égalité, la sémantique souhaitée est la suivante: "ces deux quantités sont-elles égales dans les barres d'erreur spécifiées par chaque mesure?"

Nous avons donc maintenant une réponse à votre question. Ce que vous devez faire est de garder une trace de l’erreur sur chaque quantité; Par exemple, la hauteur du bâtiment est "comprise entre 0,01 et 123,56 mètres", car vous savez à quel point la mesure est précise. Si vous obtenez alors une autre mesure 123,5587 et que vous voulez savoir si les deux mesures sont "égales" dans les tolérances d'erreur, effectuez la soustraction et voyez si elle correspond à la tolérance d'erreur. Dans ce cas, c'est le cas. Si les mesures étaient en fait précises au micromètre, elles ne sont pas égales. 

En bref: vous êtes la seule personne ici à savoir ce que sont les tolérances d'erreur raisonnables, car vous êtes la seule personne à savoir d'où proviennent les chiffres que vous manipulez. Utilisez la tolérance d'erreur la plus appropriée pour vos mesures, compte tenu de la précision du matériel que vous avez utilisé pour le produire.

44
Eric Lippert

Si vous avez deux valeurs doubles proches de 1,0, mais qu'elles ne diffèrent que par leurs bits les moins significatifs, leur différence sera alors de plusieurs ordres de grandeur supérieurs à Double.Epsilon. En fait, la différence est de 324 ordres de grandeur décimaux. Cela est dû à l’effet de la partie exposant. Double.Epsilon a un exposant négatif énorme sur lui, alors que 1.0 a un exposant de zéro (après élimination des biais, bien sûr).

Si vous souhaitez comparer deux valeurs similaires pour l'égalité, vous devez choisir une valeur epsilon personnalisée appropriée pour la taille des ordres de grandeur des valeurs à comparer.

Si les valeurs doubles que vous comparez sont proches de 1.0. La valeur du bit le moins significatif serait alors proche de 0,0000000000000001. Si les valeurs doubles que vous comparez sont exprimées en quadrillions, la valeur du bit le moins significatif pourrait atteindre un millier. Aucune valeur unique pour epsilon ne peut être utilisée pour les comparaisons d'égalité dans ces deux cas.

9

Je viens de faire ceci - en utilisant l’idée de Kent Bogarts.

private bool IsApproximatelyEqual(double x, double y, double acceptableVariance)
{
     double variance = x > y ? x - y : y - x;
     return variance < acceptableVariance;

     //or
     //return Math.Abs(x - y) < acceptableVariance;
}
6
Matt Canty

Il peut être utilisé pour des comparaisons, en supposant que vous voulez vous assurer que les deux valeurs sont exactement égales ou ont la plus petite différence représentable pour le type double. De manière générale, vous voudriez utiliser un nombre supérieur à double.Epsilon pour vérifier si deux doublons sont approximativement égaux.

Pourquoi le framework .NET ne définit pas quelque chose comme

bool IsApproximatelyEqual(double value, double permittedVariance);

est au delà de moi.

4
Kent Boogaart

Je pense que les éléments pertinents du lien MSDN que vous avez posté sont les suivants:

Cependant, la propriété Epsilon n'est pas un mesure générale de la précision du Type double; il s’applique uniquement à Double instances qui ont une valeur de zéro.

Remarque: la valeur de l’Epsilon propriété n'est pas équivalent à machine epsilon, qui représente le supérieur borne de l'erreur relative due à arrondi en arithmétique en virgule flottante.

Cette valeur n'est pas définie comme la plus petite nombre positif x, tel que x + 1,0 n'est pas égal à 1.0, donc Double.Epsilon ne peut pas être utilisé pour "presque égalité" . Il n'y a pas de constante dans le cadre dont la valeur est la plus petite nombre positif x, tel que x + 1,0 n'est pas égal à 1.0.

Je dois dire que cela me surprend. Moi aussi j'avais supposé que Double.Epsilon était l'équivalent de DBL_EPSILON dans c/c ++ - clairement pas! 

D'après ce que je peux lire à propos de ce lien, il semble dire "vous devez déterminer vous-même une valeur décente pour les comparaisons", ce qui est pour le moins surprenant.
Peut-être que quelqu'un de plus informé peut clarifier :)

3
zebrabox

J'utilise le suivant 

public static class MathUtil {
    /// <summary>
    /// smallest such that 1.0+EpsilonF != 1.0
    /// </summary>
    public const float EpsilonF = 1.192092896e-07F;

    /// <summary>
    /// smallest such that 1.0+EpsilonD != 1.0
    /// </summary>
    public const double EpsilonD = 2.2204460492503131e-016;

    [MethodImpl( MethodImplOptions.AggressiveInlining )]
    public static bool IsZero( this double value ) {
        return value < EpsilonD && value > -EpsilonD;
    }

    [MethodImpl( MethodImplOptions.AggressiveInlining )]
    public static int Sign( this double value ) {
        if ( value < -EpsilonD ) {
            return -1;
        }
        if ( value > EpsilonD )
            return 1;
        return 0;
    }

et si vous voulez vérifier l’égalité des deux doubles 'a' et 'b', vous pouvez utiliser

(a-b).IsZero();

et si vous voulez obtenir le résultat de la comparaison, utilisez

(a-b).Sign();
2
Panos Theof

Le problème avec la comparaison des doublons est que lorsque vous effectuez une comparaison entre deux résultats mathématiques différents égaux mais qui, en raison d'erreurs d'arrondi, n'évaluent pas la même valeur, ils auront une différence ... qui est supérieure à epsilon , sauf sur les cas Edge. Et utiliser une valeur epsilon fiable est également difficile. Certaines personnes considèrent deux doubles égaux si la différence entre eux est inférieure à une valeur en pourcentage, car l'utilisation d'une différence minimale statique, epsilon, peut signifier que vos différences sont trop petites ou trop grandes lorsque le double lui-même est élevé ou faible.

1
Brian

Vous n'avez pas le choix de le calculer vous-même ou de définir votre propre constante.

double calculateMachineEpsilon() {
    double result = 1.0;
    double one = 1.0/256;

    while(one + result/2.0 != 1.0) {
        result/=2.0;
    }
    return result;
}
0
Marek R

Voici du code qui a été inclus deux fois dans Silverlight Control Toolkit:

    public static bool AreClose(double value1, double value2)
    {
        //in case they are Infinities (then epsilon check does not work)
        if(value1 == value2) return true;
        // This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON
        double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON;
        double delta = value1 - value2;
        return(-eps < delta) && (eps > delta);
    }

À un endroit, ils utilisent 1e-6 pour epsilon; dans un autre, ils utilisent 1.192093E-07. Vous voudrez choisir votre propre epsilon.

0
Gabe