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".
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.