Java Language Comparer des valeurs en virgule flottante


Exemple

Vous devez faire attention lorsque vous comparez des valeurs à virgule flottante ( float ou double ) en utilisant des opérateurs relationnels: == != , < Et ainsi de suite. Ces opérateurs donnent des résultats en fonction des représentations binaires des valeurs à virgule flottante. Par exemple:

public class CompareTest {
    public static void main(String[] args) {
        double oneThird = 1.0 / 3.0;
        double one = oneThird * 3;
        System.out.println(one == 1.0);      // prints "false"
    }
}

Le calcul oneThird a introduit une erreur d’arrondi minuscule, et lorsque nous multiplions un oneThird par 3 nous obtenons un résultat légèrement différent de 1.0 .

Ce problème des représentations inexactes est plus flagrant lorsque nous essayons de mélanger double et float dans les calculs. Par exemple:

public class CompareTest2 {
    public static void main(String[] args) {
        float floatVal = 0.1f;
        double doubleVal = 0.1;
        double doubleValCopy = floatVal;

        System.out.println(floatVal);      // 0.1
        System.out.println(doubleVal);     // 0.1
        System.out.println(doubleValCopy); // 0.10000000149011612
        
        System.out.println(floatVal == doubleVal); // false
        System.out.println(doubleVal == doubleValCopy); // false
    }
}

Les représentations en virgule flottante utilisées en Java pour les types float et double ont un nombre limité de chiffres de précision. Pour le type float , la précision est de 23 chiffres binaires ou d'environ 8 chiffres décimaux. Pour le type double , il s'agit de 52 bits ou d'environ 15 chiffres décimaux. De plus, certaines opérations arithmétiques introduiront des erreurs d'arrondi. Par conséquent, lorsqu'un programme compare des valeurs à virgule flottante, il est pratique courante de définir un delta acceptable pour la comparaison. Si la différence entre les deux nombres est inférieure au delta, ils sont considérés égaux. Par exemple

if (Math.abs(v1 - v2) < delta)

Delta comparer exemple:

public class DeltaCompareExample {

    private static boolean deltaCompare(double v1, double v2, double delta) {
        // return true iff the difference between v1 and v2 is less than delta
        return Math.abs(v1 - v2) < delta;
    }
    
    public static void main(String[] args) {
        double[] doubles = {1.0, 1.0001, 1.0000001, 1.000000001, 1.0000000000001};
        double[] deltas = {0.01, 0.00001, 0.0000001, 0.0000000001, 0};

        // loop through all of deltas initialized above
        for (int j = 0; j < deltas.length; j++) {
            double delta = deltas[j];
            System.out.println("delta: " + delta);

            // loop through all of the doubles initialized above
            for (int i = 0; i < doubles.length - 1; i++) {
                double d1 = doubles[i];
                double d2 = doubles[i + 1];
                boolean result = deltaCompare(d1, d2, delta);

                System.out.println("" + d1 + " == " + d2 + " ? " + result);
                
            }

            System.out.println();
        }
    }
}

Résultat:

delta: 0.01
1.0 == 1.0001 ? true
1.0001 == 1.0000001 ? true
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true

delta: 1.0E-5
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true

delta: 1.0E-7
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true

delta: 1.0E-10
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false

delta: 0.0
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false

Pour la comparaison des types primitifs double et float on peut également utiliser une méthode de compare statique du type boxe correspondant. Par exemple:

double a = 1.0;
double b = 1.0001;

System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1

Enfin, il peut être difficile de déterminer quels sont les deltas les plus appropriés pour une comparaison. Une approche couramment utilisée consiste à choisir des valeurs delta qui, selon notre intuition, sont à peu près correctes. Cependant, si vous connaissez l'échelle et la précision des valeurs d'entrée, ainsi que les calculs effectués, il est possible de définir mathématiquement des limites solides sur la précision des résultats et, par conséquent, sur les deltas. (Il existe une branche formelle des mathématiques connue sous le nom d’analyse numérique qui était enseignée à des scientifiques spécialisés dans ce type d’analyse.)