web-dev-qa-db-fra.com

Comment comparer cette séquence de doubles sont-ils tous "approximativement égaux" en Java?

J'ai une méthode en Java qui renvoie un nombre double et je veux comparer chaque nombre double renvoyé chaque fois que j'appelle la méthode (par exemple 5 fois), afin de pouvoir en conclure que le nombre renvoyé est presque identique à chaque fois.

Comment puis-je faire ceci?

19
FranXh

Vous devez d’abord décider de ce que «presque identique» signifie. Par exemple, il existe une méthode dans Java.lang.Math appelée ulp () qui, à partir d’un double, renvoie la distance entre ce double et le suivant; c'est-à-dire la plus petite différence possible entre ce nombre et un autre. Vous pouvez simplement comparer la différence entre les deux doubles et le résultat de l'appel de cette méthode.

D'autre part, vous voulez peut-être que deux nombres soient juste à moins de 1% l'un de l'autre. Dans ce cas, effectuez le même calcul, mais utilisez le premier nombre multiplié par 0.01 au lieu de ulp() comme distance acceptable la plus grande.

8
public static boolean almostEqual(double a, double b, double eps){
    return Math.abs(a-b)<eps;
}

Où eps est la mesure de l'égalité.

33
Ano

L'égalité approximative est définie en termes de différence absolue: si une différence absolue ne dépasse pas un certain nombre, probablement petit, vous pouvez alors dire que les valeurs que vous comparez sont "suffisamment proches".

double diff = Math.abs(actual - expected);
if (diff < 1E-7) {
    // Numbers are close enough
}

Vous devez faire très attention à ne pas confondre "assez proche" fin "égaux", car les deux sont fondamentalement différents: l'égalité est transitive (c'est-à-dire que a == b et b == c impliquent ensemble que a == c), tandis que "fermer Assez "n'est pas transitif.

8
dasblinkenlight

Cela dépend de ce que vous entendez par similaire. Si vous souhaitez comparer deux nombres avec une erreur absolue, par ex. 1e-6, vous pouvez utiliser epsilon. Si vous souhaitez comparer deux double quelle que soit l’échelle. par exemple. 1.1e-20 et 1.3e-20 ne sont pas similaires, mais 1.1e20 et 1.1e20 + 1e5 permettent de comparer la valeur brute.

public static void main(String... args) throws IOException {
    test(1.1e-20, 1.3e-20);
    test(1.1e20, 1.1e20 + 1e5);
}

private static void test(double a, double b) {
    System.out.println(a + " and " + b + ", similar= " + similarUnscaled(a, b, 10));
}

public static boolean similarUnscaled(double a, double b, long representationDifference) {
    long a2 = Double.doubleToRawLongBits(a);
    long b2 = Double.doubleToRawLongBits(b);
    // avoid overflow in a2 - b2
    return ((a2 >= 0) == (b2 >= 0)) &&
            Math.abs(a2 - b2) <= representationDifference;
}

empreintes

1.1E-20 and 1.3E-20, similar= false
1.1E20 and 1.100000000000001E20, similar= true
4
Peter Lawrey

Vous pouvez utiliser la méthode Guava et DoubleMath#fuzzyEquals (à partir de la version 13.0):

public static boolean fuzzyEquals(double a, double b, double tolerance)

Renvoie true si a et b se situent dans la plage de tolérance. Techniquement, cela équivaut à Math.abs (a - b) <= Tolérance || Double.valueOf (a) .equals (Double.valueOf (b)).

Les cas particuliers notables incluent:

Lien vers docs: https://google.github.io/guava/releases/17.0/api/docs/com/google/common/math/DoubleMath.html

2
Przemek Nowak

Qu'est-ce que cela signifie pour deux doubles d'être "à peu près égaux?" Cela signifie que les doubles sont dans une certaine tolérance les uns des autres. La taille de cette tolérance, qu'elle soit exprimée en nombre absolu ou en pourcentage des deux doublons, dépend de votre application.

Par exemple, deux photos affichées sur une visionneuse de photos ont approximativement la même largeur en pouces si elles occupent le même nombre de pixels à l'écran. Votre tolérance correspond donc à un nombre absolu calculé en fonction de la taille de pixel de votre écran. Par ailleurs, les bénéfices de deux entreprises financières sont probablement "à peu près égaux" s'ils se situent à moins de 0,1% l'un de l'autre. Ce ne sont que des exemples hypothétiques, mais le fait est que cela dépend de votre application.

Maintenant pour une mise en œuvre. Supposons que votre application appelle une tolérance absolue. Ensuite, vous pouvez utiliser

private static final double TOLERANCE = 0.00001;

public static boolean approxEqual(final double d1, final double d2) {
    return Math.abs(d1 - d2) < TOLERANCE;
}

comparer deux doubles et utiliser

approxEqual(d1, d2) && approxEqual(d1, d3) && approxEqual(d1, d4) && approxEqual(d1, d5)

comparer cinq doubles.

1
Adam Mihalcin