¿Cómo puedo comparar correctamente los valores dobles para la igualdad en una prueba unitaria?

20

Recientemente diseñé un módulo de series temporales donde mi serie temporal es esencialmente a SortedDictionnary<DateTime, double>.

Ahora me gustaría crear pruebas unitarias para asegurarme de que este módulo siempre funcione y produzca el resultado esperado.

Una operación común es calcular el rendimiento entre los puntos en la serie de tiempo.

Entonces, lo que hago es crear una serie temporal con, digamos, {1.0, 2.0, 4.0} (en algunas fechas), y espero que el resultado sea {100%, 100%}.

La cuestión es que si creo manualmente una serie temporal con los valores {1.0, 1.0} y verifico la igualdad (al comparar cada punto), la prueba no pasaría, ya que siempre habrá imprecisiones al trabajar con representaciones binarias de valores reales. números.

Por lo tanto, decidí crear la siguiente función:

private static bool isCloseEnough(double expected, double actual, double tolerance=0.002)
{
    return squaredDifference(expected, actual) < Math.Pow(tolerance,2);
}

¿Hay otra forma común de lidiar con tal caso?

SRKX
fuente

Respuestas:

10

Se me ocurren otras dos formas de abordar este problema:

Puedes usar Is.InRange:

Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));

Puedes usar Math.Round:

Assert.That(Math.Round(result, sigDigits), Is.EqualTo(expected));

Creo que ambas formas son más expresivas que una función dedicada, porque el lector puede ver con precisión qué sucede con su número antes de que se compare con el valor esperado.

dasblinkenlight
fuente
2
Solo una nota de que esta respuesta es específica de NUnit y muestra el modelo de afirmación "basado en restricciones". El modelo de afirmación clásico se vería así: Assert.AreEqual (esperado, real, tolerancia);
RichardM
1
@ RichardM: Publique eso como respuesta y lo seleccionaré, acéptelo.
SRKX
La respuesta de @dasblinkenlight es correcta, solo agrega algunos detalles (ya que puede no estar claro: el modelo de afirmación clásico también es NUnit). Es probable que otros marcos de prueba (no MSTest) tengan su propio modelo de afirmación para tratar con valores de coma flotante.
RichardM
1
Assert.That(result, Is.InRange(expected-tolerance, expected+tolerance));fallará si tolerance/abs(expected) < 1E-16.
quant_dev
@quant_dev Tienes toda la razón. Como OP habla de calcular los retornos como porcentajes, supuse que abs(expected)serían de uno a dos dígitos. También asumí la tolerancia en las cercanías de 1E-9. Bajo estos supuestos, este enfoque ciertamente simplista podría servirle razonablemente bien (lo uso Is.InRangeen mis pruebas).
dasblinkenlight
3

Depende de lo que hagas con los números. Si está probando un método que se supone, por ejemplo, selecciona un valor apropiado de un conjunto de entrada basado en algunos criterios, entonces debe probar la igualdad estricta. Si está haciendo cálculos de punto flotante, por lo general deberá realizar una prueba con una tolerancia distinta de cero. El tamaño de la tolerancia depende de los cálculos, pero con doble precisión, un buen punto de partida es elegir la tolerancia relativa 1E-14 para cálculos simples y 1E-8 (tolerancia) para los más complicados. YMMV, por supuesto, y debe agregar una pequeña tolerancia absoluta si el resultado esperado es 0.

cuant_dev
fuente