Ho una funzione numerica che f(x, y)
restituisce un doppio numero in virgola mobile che implementa una formula e voglio verificare che sia corretta rispetto alle espressioni analitiche per tutte le combinazioni dei parametri x
e y
che mi interessa. Qual è il modo corretto di confrontare il calcolato e numeri analitici in virgola mobile?
Diciamo che i due numeri sono a
e b
. Finora mi sono assicurato che gli errori sia assoluti ( abs(a-b) < eps
) che relativi ( abs(a-b)/max(abs(a), abs(b)) < eps
) siano inferiori a eps. In questo modo rileverà imprecisioni numeriche anche se i numeri sono diciamo intorno a 1e-20.
Tuttavia, oggi ho scoperto un problema, il valore numerico a
e il valore analitico b
erano:
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
Quindi l'errore assoluto [50] è (ovviamente) piccolo, ma l'errore relativo [52] è grande. Quindi ho pensato di avere un bug nel mio programma. Con il debug, mi sono reso conto che questi numeri sono denormali . Come tale, ho scritto la seguente routine per fare il confronto relativo corretto:
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
Dove tiny(1._dp)
restituisce 2.22507385850720138E-308 sul mio computer. Ora tutto funziona e ottengo semplicemente 0 come errore relativo e tutto è ok. In particolare, l'errore relativo [52] sopra riportato è errato, è semplicemente causato da un'accuratezza insufficiente dei numeri denormali. La mia implementazione della rel_error
funzione è corretta? Devo solo controllare che abs(a-b)
sia inferiore a tiny (= denormal) e restituire 0? O dovrei controllare qualche altra combinazione, come
max(abs(a), abs(b))
?
Vorrei solo sapere qual è il modo "corretto".
exp(log_gamma(m+0.5_dp) - (m+0.5_dp)*log(t)) / 2
per m = 234, t = 2000. Aumenta rapidamente a zero man mano che aumentom
. Tutto quello che voglio assicurarmi che la mia routine numerica ritorni numeri "corretti" (per restituire lo zero anche perfettamente bene) ad almeno 12 cifre significative. Quindi, se il calcolo restituisce un numero denormale, è semplicemente zero e non dovrebbero esserci problemi. Quindi solo la routine di confronto deve essere robusta contro questo.