Tengo una función numérica que f(x, y)
devuelve un número de coma flotante doble que implementa alguna fórmula y quiero verificar que sea correcta contra las expresiones analíticas para todas las combinaciones de los parámetros x
y y
que me interese. ¿Cuál es la forma correcta de comparar lo calculado y números analíticos de coma flotante?
Digamos que los dos números son a
y b
. Hasta ahora me he asegurado de que tanto los errores absolutos ( abs(a-b) < eps
) como los relativos ( abs(a-b)/max(abs(a), abs(b)) < eps
) sean menores que eps. De esa forma, detectará imprecisiones numéricas incluso si los números son, digamos, alrededor de 1e-20.
Sin embargo, hoy descubrí un problema, el valor numérico a
y el valor analítico b
fueron:
In [47]: a
Out[47]: 5.9781943146790832e-322
In [48]: b
Out[48]: 6.0276008792632078e-322
In [50]: abs(a-b)
Out[50]: 4.9406564584124654e-324
In [52]: abs(a-b) / max(a, b)
Out[52]: 0.0081967213114754103
Entonces, el error absoluto [50] es (obviamente) pequeño, pero el error relativo [52] es grande. Entonces pensé que tenía un error en mi programa. Al depurar, me di cuenta, que estos números son normales . Como tal, escribí la siguiente rutina para hacer la comparación relativa adecuada:
real(dp) elemental function rel_error(a, b) result(r)
real(dp), intent(in) :: a, b
real(dp) :: m, d
d = abs(a-b)
m = max(abs(a), abs(b))
if (d < tiny(1._dp)) then
r = 0
else
r = d / m
end if
end function
Donde tiny(1._dp)
devuelve 2.22507385850720138E-308 en mi computadora. Ahora todo funciona y simplemente obtengo 0 como error relativo y todo está bien. En particular, el error relativo anterior [52] es incorrecto, simplemente es causado por una precisión insuficiente de los números denormales. ¿Es rel_error
correcta mi implementación de la función? ¿Debo comprobar que abs(a-b)
es menor que minúsculo (= denormal) y devolver 0? ¿O debería comprobar alguna otra combinación, como
max(abs(a), abs(b))
?
Me gustaría saber cuál es la forma "adecuada".
fuente
exp(log_gamma(m+0.5_dp) - (m+0.5_dp)*log(t)) / 2
para m = 234, t = 2000. Va a cero rápidamente a medida que aumentom
. Todo lo que quiero es asegurarme de que mi rutina numérica devuelva números "correctos" (devolver cero también está perfectamente bien) a al menos 12 dígitos significativos. Entonces, si el cálculo devuelve un número normal, entonces es simplemente cero y no debería haber ningún problema. Entonces, solo la rutina de comparación debe ser robusta contra esto.Donald Knuth tiene una propuesta para un algoritmo de comparación de coma flotante en el volumen 2 "Algoritmos seminéricos" de "El arte de la programación de computadoras". Fue implementado en C por Th. Belding (ver paquete fcmp ) y está disponible en GSL .
fuente
abs(a-b)/max(a, b) < eps
, lo hacemosabs(a-b)/2**exponent(max(a, b)) < eps
, lo que prácticamente deja caer la mantisa en elmax(a, b)
, por lo que, en mi opinión, la diferencia es insignificante.Los números desnormalizados óptimamente redondeados pueden tener un alto error relativo. (Vaciar eso a cero mientras todavía lo llama un error relativo es engañoso).
Pero cerca de cero, calcular los errores relativos no tiene sentido.
Por lo tanto, incluso antes de alcanzar números desnormalizados, probablemente debería cambiar a una precisión absoluta (es decir, la que desea garantizar en este caso).
Entonces, los usuarios de su código saben con precisión cuánta precisión tienen realmente.
fuente
abs(a-b) < tiny(1._dp)
como lo hice anteriormente.tiny(1._dp)=2.22507385850720138E-308
(cometí un error en mi comentario anterior, es 2e-308, no 1e-320). Entonces este es mi error absoluto. Entonces necesito comparar el error relativo. Veo tu punto, creo que tienes razón. ¡Gracias!