Comparaison des valeurs doubles pour l'égalité en Java.

je voudrais des conseils de personnes qui ont plus d'expérience de travail avec l'égalité primitive double en Java. L'utilisation de d1 == d2 pour deux doubles d1 et d2 n'est pas suffisante en raison d'éventuelles erreurs d'arrondissement.

mes questions sont:

  1. Est de Java Double.compare(d1,d2) == 0 gestion des erreurs d'arrondi à un certain degré? Comme expliqué dans la 1.7 il retourne valeur 0 si d1 est numériquement égal à d2 . Est-ce que quelqu'un sait exactement ce qu'ils veulent dire par égal numériquement?

  2. en utilisant le calcul de l'erreur relative par rapport à une valeur delta, y a-t-il une valeur générique (non spécifique à l'application) de delta que vous recommanderiez? Voir l'exemple ci-dessous.

ci-dessous est une fonction générique pour vérifier l'égalité en tenant compte de l'erreur relative. Quelle valeur de delta recommanderiez-vous pour saisir la majorité des erreurs d'arrondissement des opérations simples+, -,/,*?

public static boolean isEqual(double d1, double d2) {
    return d1 == d2 || isRelativelyEqual(d1,d2);
}

private static boolean isRelativelyEqual(double d1, double d2) {
    return delta > Math.abs(d1- d2) / Math.max(Math.abs(d1), Math.abs(d2));
}
8
demandé sur chr0mzie 2014-08-06 16:26:15

3 réponses

vous pourriez expérimenter avec des valeurs delta de l'ordre de 10 -15 mais vous remarquerez que certains calculs donnent une plus grande erreur d'arrondissement. De plus, plus vous effectuerez d'opérations plus importantes, plus vous accumulerez d'erreurs d'arrondissement.

un cas particulièrement mauvais est si vous soustrayez deux nombres presque égaux, par exemple 1.00000001-1.0 et comparer le résultat à 0.0000000001

il y a donc peu d'espoir de trouver un méthode générique applicable dans toutes les situations. Vous devez toujours calculer la précision à laquelle vous pouvez vous attendre dans une certaine application et ensuite considérer les résultats égaux s'ils sont plus proches que cette précision.

par exemple la sortie de

public class Main {

    public static double delta(double d1, double d2) {
        return Math.abs(d1- d2) / Math.max(Math.abs(d1), Math.abs(d2));
    }

    public static void main(String[] args) {
        System.out.println(delta(0.1*0.1, 0.01));
        System.out.println(delta(1.0000000001 - 1.0, 0.0000000001));
    }

}

est

1.7347234759768068E-16
8.274036411668976E-8

arithmétique D'intervalle peut être utilisé pour tenir compte des erreurs d'arrondissement accumulées. Dans la pratique, cependant l'erreur les intervalles sont trop pessimistes, car les erreurs d'arrondissement s'annulent parfois.

4
répondu Henry 2014-08-06 12:56:52

De la javadoc de compareTo

  • Double.NaN est considéré par cette méthode comme étant égal à lui-même et supérieur à toutes les autres valeurs doubles (y compris Double.POSITIVE_INFINITY).
  • 0,0 d est considéré par cette méthode comme étant supérieur à -0,0 D.

vous pouvez trouver ce article très utile

Si vous voulez, vous pouvez vérifier comme

double epsilon = 0.0000001;
if      ( d <= ( 0 - epsilon ) ) { .. }
else if ( d >= ( 0 + epsilon ) ) { .. }
else { /* d "equals" zero */ }
2
répondu Rahul Tripathi 2016-08-10 22:35:38

vous pouvez essayer quelque chose comme ceci (non testé):

public static int sortaClose(double d1, double d2, int bits) {
    long bitMask = 0xFFFFFFFFFFFFFFFFL << bits;
    long thisBits = Double.doubleToLongBits(d1) & bitMask;
    long anotherBits = Double.doubleToLongBits(d2) & bitMask;

    if (thisBits < anotherBits) return -1;
    if (thisBits > anotherBits) return 1;
    return 0;                        
}

"bits" qui serait généralement de 1 à 4, en fonction du degré de précision que vous voulais de la coupure.

un raffinement serait d'ajouter 1 à la position du premier bit à mettre à zéro avant de masquer (pour" arrondi"), mais alors vous devez vous soucier de onduler tout le chemin jusqu'à passer le bit le plus significatif.

1
répondu Hot Licks 2014-08-06 15:46:52