Significado del argumento epsilon de assertEquals para valores dobles

Tengo una pregunta sobre junit assertEquals para probar valores dobles. Leyendo el doc API puedo ver:

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

Obsoleto. Utilice assertEquals (double expected, double real, double epsilon) en su lugar

¿Qué significa épsilon? (Epsilon es una letra del alfabeto griego, ¿no?).

¿Puede alguien explicarme cómo usarlo?

Epsilon es el valor por el que pueden estar apagados los 2 números. Entonces afirmará verdadero siempre que Math.abs(expected - actual) < epsilon

¿Qué versión de JUnit es esto? Solo he visto delta, no épsilon, ¡pero eso es un problema secundario!

Desde el JUnit javadoc :

delta: el delta máximo entre esperado y real para el cual ambos números se consideran iguales.

Probablemente sea excesivo, pero normalmente uso un número realmente pequeño, por ejemplo

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

Si está utilizando aserciones de hamcrest , puede usar el estándar equalTo() con dos dobles (no usa un delta). Sin embargo, si quiere un delta, puede usar closeTo() (ver javadoc ), por ej.

 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 el próximo JUnit 5 también hará delta opcional cuando se llame a assertEquals() con dos dobles. La implementación (si está interesado) es:

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

Los cálculos de coma flotante no son exactos; a menudo hay errores de redondeo y errores debido a la representación. (Por ejemplo, 0.1 no se puede representar exactamente en el punto flotante binario).

Debido a esto, la comparación directa de dos valores de coma flotante para la igualdad generalmente no es una buena idea, ya que pueden ser diferentes por una pequeña cantidad, dependiendo de cómo se calcularon.

El “delta”, como se llama en JUnit javadocs , describe la cantidad de diferencia que puede tolerar en los valores para que se los considere igual. El tamaño de este valor depende completamente de los valores que comparas. Al comparar dobles, normalmente utilizo el valor esperado dividido por 10 ^ 6.

El problema es que dos dobles pueden no ser exactamente iguales debido a problemas de precisión inherentes a los números de coma flotante. Con este valor delta, puede controlar la evaluación de la igualdad en función de un factor de error.

Además, algunos valores de coma flotante pueden tener valores especiales como NAN e -Infinity / + Infinity que pueden influir en los resultados.

Si realmente intenta comparar que dos dobles son exactamente iguales, es mejor compararlos como una representación larga.

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

O

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

Que puede tomar en cuenta estos matices.

No he profundizado en el método Assert en cuestión, pero solo puedo suponer que el anterior fue desaprobado para este tipo de problemas y el nuevo los tiene en cuenta.

Tenga en cuenta que si no está haciendo matemáticas, no hay nada de malo en afirmar valores exactos de coma flotante. Por ejemplo:

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

En este caso, quiere asegurarse de que sea realmente MIN_VALUE , no cero o -MIN_VALUE o MIN_NORMAL o algún otro valor muy pequeño. Puedes decir

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

pero esto te dará una advertencia de desaprobación. Para evitar eso, puede llamar a assertEquals(Object, Object) lugar:

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

Y, si realmente quieres parecer inteligente:

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

O simplemente puede usar las aseveraciones de estilo fluido de Hamcrest:

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

Sin embargo, si el valor que está comprobando proviene de algunos cálculos, use el épsilon.

Epsilon es una diferencia entre actual valores expected y los actual que puedes aceptar pensando que son iguales. Puede establecer .1 por ejemplo.

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