Signification de l’argument epsilon de assertEquals pour les valeurs doubles

J’ai une question à propos de junit assertEquals pour tester des valeurs doubles. Lire l’API doc Je peux voir:

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

Déconseillé. Utilisez assertEquals (double attendu, double réel, double epsilon) à la place

Que signifie la valeur epsilon? (Epsilon est une lettre de l’alphabet grec, non?).

Quelqu’un peut-il m’expliquer comment l’utiliser?

Epsilon est la valeur par laquelle les 2 nombres peuvent être désactivés. Donc, il affirmera vrai tant que Math.abs(expected - actual) < epsilon

De quelle version de JUnit s’agit-il? Je n’ai jamais vu de delta, pas d’epsilon – mais c’est un problème secondaire!

Du javadoc JUnit:

delta – le delta maximal entre prévu et réel pour lequel les deux nombres sont toujours considérés comme égaux.

C’est probablement exagéré, mais j’utilise généralement un très petit nombre, par exemple

 private static final double DELTA = 1e-15; @Test public void testDelta(){ assertEquals(123.456, 123.456, DELTA); } 

Si vous utilisez des assertions hamcrest , vous pouvez simplement utiliser la valeur standard equalTo() avec deux doubles (elle n’utilise pas de delta). Cependant, si vous voulez un delta, vous pouvez simplement utiliser closeTo() (voir javadoc ), par exemple

 private static final double DELTA = 1e-15; @Test public void testDelta(){ assertThat(123.456, equalTo(123.456)); assertThat(123.456, closeTo(123.456, DELTA)); } 

FYI, la prochaine JUnit 5 rendra également delta facultatif lors de l’appel d’ assertEquals() avec deux doubles. L’ implémentation (si cela vous intéresse) est:

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

Les calculs en virgule flottante ne sont pas exacts – il y a souvent des erreurs d’arrondi et des erreurs dues à la représentation. (Par exemple, 0.1 ne peut pas être exactement représenté en virgule flottante.)

De ce fait, la comparaison directe de deux valeurs à virgule flottante pour l’égalité n’est généralement pas une bonne idée, car elles peuvent différer légèrement, en fonction de la manière dont elles ont été calculées.

Le “delta”, tel qu’il est appelé dans les javadocs JUnit, décrit la quantité de différence que vous pouvez tolérer dans les valeurs pour qu’elles soient toujours considérées comme égales. La taille de cette valeur dépend entièrement des valeurs que vous comparez. Lorsque vous comparez des doubles, j’utilise généralement la valeur attendue divisée par 10 ^ 6.

Le fait est que deux doubles ne peuvent pas être exactement égaux en raison de problèmes de précision inhérents aux nombres à virgule flottante. Avec cette valeur delta, vous pouvez contrôler l’évaluation de l’égalité en fonction d’un facteur d’erreur.

De plus, certaines valeurs à virgule flottante peuvent avoir des valeurs spéciales comme NAN et -Infinity / + Infinity, qui peuvent influencer les résultats.

Si vous avez vraiment l’intention de comparer que deux doubles sont exactement égaux, il est préférable de les comparer comme une représentation longue.

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

Ou

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

Qui peut prendre en compte ces nuances.

Je ne me suis pas penché sur la méthode Assert en question, mais je ne peux que supposer que le précédent était obsolète pour ce type de problèmes et que le nouveau les prend en compte.

Notez que si vous ne faites pas de calcul, il n’y a rien de mal à affirmer des valeurs à virgule flottante exactes. Par exemple:

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

Dans ce cas, vous voulez vous assurer que c’est vraiment MIN_VALUE , pas zéro ou -MIN_VALUE ou MIN_NORMAL ou une autre très petite valeur. Tu peux dire

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

mais cela vous donnera un avertissement de dépréciation. Pour éviter cela, vous pouvez appeler assertEquals(Object, Object) place:

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

Et si vous voulez vraiment avoir l’air intelligent:

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

Ou vous pouvez simplement utiliser les assertions Hamcrest de style courant:

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

Si la valeur que vous vérifiez provient de quelques calculs, utilisez l’epsilon.

Epsilon est une différence entre actual valeurs expected et actual que vous pouvez accepter en pensant qu’elles sont égales. Vous pouvez définir .1 par exemple.

 Assert.assertTrue(Math.abs(actual-expected) == 0)