web-dev-qa-db-fra.com

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

J'ai une question à propos de junit assertEquals pour tester les valeurs doubles. En lisant le document API, je peux voir:

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

Obsolète 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?

181
elf

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

191
jberg

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

De la JUnit javadoc :

delta - le delta maximum entre les résultats attendus et réels 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 norme 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));
}

Pour info, le prochain JUnit 5 sera aussi rendre le delta optionnel lors de l'appel de assertEquals() avec deux doubles. Le implémentation (si vous êtes intéressé) est:

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

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

Pour cette raison, comparer directement deux valeurs à virgule flottante pour déterminer l’égalité n’est généralement pas une bonne idée, car elles peuvent être légèrement différentes, en fonction de la méthode utilisée pour les calculer.

Le "delta", comme il est appelé dans JUnit javadocs , décrit l'ampleur de la différence que vous pouvez tolérer dans les valeurs si elles sont toujours considérées comme égales. La taille de cette valeur dépend entièrement des valeurs que vous comparez. Lorsque je compare des doublons, j’utilise généralement la valeur attendue divisée par 10 ^ 6.

55
mdma

Le fait est que deux doubles peuvent ne pas être exactement égaux en raison de problèmes de précision inhérents aux nombres en 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 en virgule flottante peuvent avoir des valeurs spéciales telles que NAN et -Infinity/+ Infinity qui peuvent influer sur 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 sous forme de représentation longue

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

Ou

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

Ce qui peut prendre en compte ces nuances.

Je n'ai pas approfondi la méthode Assert en question, mais je ne peux que supposer que la précédente était obsolète pour ce type de problème et que la nouvelle en tenait compte.

11
Edwin Dalorzo

Notez que si vous ne faites pas de maths, il n’ya 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 qu'il s'agit bien de MIN_VALUE et non de zéro ou -MIN_VALUE ou de MIN_NORMAL ou d'une autre valeur très petite. 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) à la 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 malin:

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

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

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

Si la valeur que vous vérifiez fait provient d'un calcul mathématique, utilisez epsilon.

2
David Moles

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

1
Constantiner