Bedeutung des epsilon-Arguments von assertEquals für doppelte Werte

187

Ich habe eine Frage zu junit assertEquals, um Doppelwerte zu testen. Beim Lesen des API-Dokuments kann ich Folgendes sehen:

@Deprecated
public static void assertEquals(double expected, double actual)

Veraltet. Verwenden Sie stattdessen assertEquals (doppelt erwartet, doppelt tatsächlich, doppelt epsilon)

Was bedeutet der epsilonWert? (Epsilon ist ein Buchstabe im griechischen Alphabet, oder?)

Kann mir jemand erklären, wie man es benutzt?

Édipo Féderle
quelle

Antworten:

198

Epsilon ist der Wert, um den die beiden Zahlen abweichen können. So wird es sich als wahr erweisen, solangeMath.abs(expected - actual) < epsilon

jberg
quelle
3
Welchen Wert sollte ich als Epsilon übergeben?
Smaragdhieu
15
@ Emerald214 die Menge an Präzision. Wenn Sie behaupten möchten, dass ein doppelter Wert 0D ist, wäre epsilon 0 (100% Genauigkeit, keine Ausnahmen). Wenn Sie eine Fehlerquote wünschen (z. B. für Grad), können Sie epsilon auf 1 setzen, was bedeutet, dass beispielsweise 64,2 ° 64,8 ° entspricht (da abs (64,8-64,2) <1)
Pieter De Bie
3
In der Dokumentation heißt es: "Delta - das maximale Delta zwischen erwartet und tatsächlich, für das beide Zahlen immer noch als gleich angesehen werden." Also ich denke das sollte ein <=nicht sein <.
Andrew Cheong
Wenn ich mir den Code anschaue, sehe ich, dass er die Methode doubleIsDifferent(zum Vergleichen von Doppelwerten) aufruft und zurückgibt Math.abs(d1 - d2) > delta. Wenn also die Differenz zwischen d1 und d2 höher als Delta ist, bedeutet dies, dass die Werte unterschiedlich sind und true zurückgeben. Es wird false zurückgegeben, wenn die Werte als gleich angesehen werden. Diese Methode wird in den assertEquals direkt aufgerufen. Wenn sie true zurückgibt, werden die assertEquals aufgerufen failNotEqualsund das Testergebnis ist ein Fehler.
Anthomaxcool
1
@jbert Kann jemand raten, was ein typischer Epsilon-Doppelwert wäre, wenn ich nur mit der Mittelung vieler Zahlen oder Standardabweichungen arbeiten würde?
Simgineer
121

Welche Version von JUnit ist das? Ich habe immer nur Delta gesehen, kein Epsilon - aber das ist ein Nebenproblem!

Aus dem JUnit Javadoc :

Delta - das maximale Delta zwischen erwartet und tatsächlich, für das beide Zahlen immer noch als gleich angesehen werden.

Es ist wahrscheinlich übertrieben, aber ich benutze normalerweise eine sehr kleine Zahl, z

private static final double DELTA = 1e-15;

@Test
public void testDelta(){
    assertEquals(123.456, 123.456, DELTA);
}

Wenn Sie Hamcrest- Assertions verwenden, können Sie den Standard nur equalTo()mit zwei Doppelwerten verwenden (es wird kein Delta verwendet). Wenn Sie jedoch ein Delta möchten, können Sie es einfach verwenden closeTo()(siehe javadoc ), z

private static final double DELTA = 1e-15;

@Test
public void testDelta(){
    assertThat(123.456, equalTo(123.456));
    assertThat(123.456, closeTo(123.456, DELTA));
}

Zu Ihrer Information, die kommende JUnit 5 wird Delta auch optional machen, wenn Sie assertEquals()mit zwei Doppel anrufen . Die Implementierung (wenn Sie interessiert sind) ist:

private static boolean doublesAreEqual(double value1, double value2) {
    return Double.doubleToLongBits(value1) == Double.doubleToLongBits(value2);
}
James Bassett
quelle
57

Gleitkommaberechnungen sind nicht genau - es gibt häufig Rundungsfehler und Fehler aufgrund der Darstellung. (Zum Beispiel kann 0.1 nicht genau im binären Gleitkomma dargestellt werden.)

Aus diesem Grund ist es normalerweise keine gute Idee, zwei Gleitkommawerte direkt auf Gleichheit zu vergleichen, da sie sich je nach Berechnung geringfügig unterscheiden können.

Das "Delta", wie es in den JUnit- Javadocs genannt wird , beschreibt den Unterschied, den Sie in den Werten tolerieren können, damit sie immer noch als gleich angesehen werden. Die Größe dieses Werts hängt vollständig von den Werten ab, die Sie vergleichen. Beim Vergleichen von Doppelwerten verwende ich normalerweise den erwarteten Wert geteilt durch 10 ^ 6.

mdma
quelle
11

Die Sache ist, dass zwei Doppel aufgrund von Genauigkeitsproblemen, die Gleitkommazahlen inhärent sind, möglicherweise nicht genau gleich sind. Mit diesem Delta-Wert können Sie die Bewertung der Gleichheit anhand eines Fehlerfaktors steuern.

Einige Gleitkommawerte können auch spezielle Werte wie NAN und -Infinity / + Infinity haben, die die Ergebnisse beeinflussen können.

Wenn Sie wirklich vergleichen möchten, dass zwei Doppel genau gleich sind, vergleichen Sie sie am besten als lange Darstellung

Assert.assertEquals(Double.doubleToLongBits(expected), Double.doubleToLongBits(result));

Oder

Assert.assertEquals(0, Double.compareTo(expected, result));

Welches kann diese Nuancen berücksichtigen.

Ich habe mich nicht mit der fraglichen Assert-Methode befasst, kann aber nur davon ausgehen, dass die vorherige für diese Art von Problemen veraltet war und die neue sie berücksichtigt.

Edwin Dalorzo
quelle
2

Epsilon ist ein Unterschied zwischen expectedund actualWerten, die Sie akzeptieren können, wenn Sie glauben, dass sie gleich sind. Sie können .1zum Beispiel einstellen .

Constantiner
quelle
2

Beachten Sie, dass es nichts Falsches ist, wenn Sie keine genauen Gleitkommawerte angeben, wenn Sie nicht rechnen. Zum Beispiel:

public interface Foo {
    double getDefaultValue();
}

public class FooImpl implements Foo {
    public double getDefaultValue() { return Double.MIN_VALUE; }
}

In diesem Fall wollen Sie sicherstellen , es ist wirklich MIN_VALUEnicht Null oder -MIN_VALUEoder MIN_NORMALoder ein anderer sehr kleiner Wert. Sie können sagen

double defaultValue = new FooImpl().getDefaultValue();
assertEquals(Double.MIN_VALUE, defaultValue);

Dies führt jedoch zu einer Abwertungswarnung. Um dies zu vermeiden, können Sie assertEquals(Object, Object)stattdessen anrufen :

// really you just need one cast because of autoboxing, but let's be clear
assertEquals((Object)Double.MIN_VALUE, (Object)defaultValue);

Und wenn Sie wirklich schlau aussehen wollen:

assertEquals(
    Double.doubleToLongBits(Double.MIN_VALUE), 
    Double.doubleToLongBits(defaultValue)
);

Oder Sie können einfach Behauptungen im fließenden Stil von Hamcrest verwenden:

// equivalent to assertEquals((Object)Double.MIN_VALUE, (Object)defaultValue);
assertThat(defaultValue, is(Double.MIN_VALUE));

Wenn der Wert, den Sie überprüfen, jedoch aus Berechnungen stammt, verwenden Sie das Epsilon.

David Moles
quelle
7
Wenn Sie genau gleich prüfen möchten, setzen Sie epsilon auf 0.0 - die Objektvariante ist nicht erforderlich.
Mel Nicholson
-2
Assert.assertTrue(Math.abs(actual-expected) == 0)
Prakash
quelle
Bei Verwendung von Gleitkommazahlen (wie Float oder Double) funktioniert dies nicht zuverlässig. Möglicherweise möchten Sie überprüfen, wie Gleitkommazahlen in Java gespeichert sind und wie die arithmetischen Operationen auf sie funktionieren. (Spoiler: Erwarten Sie einige Rundungsfehler!)
Attila