Estoy trabajando en un proyecto de visualización de computación científica & en C# /. NET, y usamos double
s para representar todas las cantidades físicas. Puesto que los números de punto flotante siempre incluyen un poco de redondeo, tenemos métodos sencillos para hacer comparaciones de igualdad, tales como:Tratando con los errores de coma flotante en .NET
static double EPSILON = 1e-6;
bool ApproxEquals(double d1, double d2) {
return Math.Abs(d1 - d2) < EPSILON;
}
bastante estándar.
Sin embargo, constantemente nos encontramos teniendo que ajustar la magnitud de EPSILON
cuando nos encontramos con situaciones en las que el error de cantidades "iguales" es mayor de lo que habíamos anticipado. Por ejemplo, si multiplica 5 grandes double
juntos y luego los divide 5 veces, perderá mucha precisión. Ha llegado al punto en que no podemos hacer que EPSILON sea demasiado grande o nos dará falsos positivos, pero también obtenemos falsos negativos.
En general, nuestro enfoque ha sido buscar más algoritmos numéricamente estables para trabajar, pero el programa es muy computacional y hay mucho que hemos podido hacer.
¿Alguien tiene alguna buena estrategia para resolver este problema? He analizado un poco el tipo Decimal
, pero estoy preocupado por el rendimiento y realmente no sé lo suficiente sobre él para saber si resolvería el problema o si lo ocultaría. Estaría dispuesto a aceptar un golpe de rendimiento moderado (digamos, 2x) yendo a Decimal
si solucionara estos problemas, pero el rendimiento es definitivamente una preocupación y dado que el código está mayormente limitado por la aritmética de coma flotante, no creo es una preocupación irracional. He visto personas que citan una diferencia de 100 veces, lo que definitivamente sería inaceptable.
Además, cambiar a Decimal
tiene otras complicaciones, como la falta general de soporte en la biblioteca Math
, por lo que tendríamos que escribir nuestra propia función de raíz cuadrada, por ejemplo.
¿Algún consejo?
EDIT: Por cierto, el hecho de que estoy usando un epsilon constante (en lugar de una comparación relativa) no es el punto de mi pregunta. Acabo de poner eso como un ejemplo, no es en realidad un snippit de mi código. Cambiar a una comparación relativa no supondría una diferencia para la pregunta, porque el problema surge al perder precisión cuando los números se vuelven muy grandes y luego pequeños nuevamente. Por ejemplo, podría tener un valor de 1000 y luego hacer una serie de cálculos que deberían dar como resultado exactamente el mismo número, pero debido a la pérdida de precisión, en realidad tengo 1001. Si luego voy a comparar esos números, no lo hago. Importa mucho si uso una comparación relativa o absoluta (siempre que haya definido las comparaciones de una manera que sea significativa para el problema y la escala).
De todos modos, como sugirió Mitch Wheat, el reordenamiento de los algoritmos ayudó a resolver los problemas.
@nobugz: si lo sientes así, borra y edita tu respuesta y yo eliminaré el voto a favor. –
@ High-Performance Mark: ¿este comentario fue para OP? –
Bueno, he estado revisando nuestros algoritmos y haciendo algunos reordenamientos como sugirió, y parece haber ayudado. Empecé a trabajar en ese artículo, ¡aunque no es exactamente una lectura ligera! Gracias por la ayuda. –