Eu tenho uma função numérica f(x, y)
retornando um número de ponto flutuante duplo que implementa alguma fórmula e quero verificar se ele está correto em relação às expressões analíticas para todas as combinações de parâmetros x
e no y
qual estou interessado. Qual é a maneira correta de comparar os valores calculados e números de ponto flutuante analítico?
Digamos que os dois números são a
e b
. Até agora, tenho me assegurado de que os erros absolutos ( abs(a-b) < eps
) e relativos ( abs(a-b)/max(abs(a), abs(b)) < eps
) sejam menores que eps. Dessa forma, ele detectará imprecisões numéricas, mesmo que os números sejam, digamos, em torno de 1e-20.
No entanto, hoje eu descobri um problema, o valor numérico a
e o valor analítico b
eram:
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
Portanto, o erro absoluto [50] é (obviamente) pequeno, mas o erro relativo [52] é grande. Então, eu pensei que eu tinha um bug no meu programa. Ao depurar, percebi, que esses números são anormais . Como tal, escrevi a seguinte rotina para fazer a comparação relativa adequada:
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
Where tiny(1._dp)
retorna 2.22507385850720138E-308 no meu computador. Agora tudo funciona e eu simplesmente recebo 0 como o erro relativo e está tudo ok. Em particular, o erro relativo acima [52] está errado, é simplesmente causado pela precisão insuficiente dos números desnormais. Minha implementação da rel_error
função está correta? Devo apenas verificar se abs(a-b)
é menor que minúsculo (= denormal) e retornar 0? Ou devo verificar alguma outra combinação, como
max(abs(a), abs(b))
?
Gostaria apenas de saber qual é o caminho "adequado".
fonte
exp(log_gamma(m+0.5_dp) - (m+0.5_dp)*log(t)) / 2
para m = 234, t = 2000. Ele chega a zero rapidamente à medida que aumentam
. Tudo o que quero ter certeza de que minha rotina numérica retorne números "corretos" (retornar zero também é perfeitamente adequado) para pelo menos 12 dígitos significativos. Portanto, se o cálculo retornar um número não normal, então é simplesmente zero e não deverá haver problema. Portanto, apenas a rotina de comparação precisa ser robusta contra isso.Donald Knuth tem uma proposta para um algoritmo de comparação de ponto flutuante no volume 2 "Algoritmos seminuméricos" de "A arte da programação de computadores". Foi implementado em C por Th. Belding (consulte o pacote fcmp ) e está disponível no GSL .
fonte
abs(a-b)/max(a, b) < eps
, nós fazemosabs(a-b)/2**exponent(max(a, b)) < eps
, que praticamente deixa cair a mantissa namax(a, b)
, então, na minha opinião, a diferença é insignificante.Números desnormalizados idealmente arredondados podem realmente ter um erro relativo alto. (Liberar isso para zero enquanto ainda o chama de erro relativo é enganoso.)
Mas quase zero, calcular erros relativos não faz sentido.
Portanto, mesmo antes de atingir números não normalizados, você provavelmente deve mudar para a precisão absoluta (a saber, o que você deseja garantir neste caso).
Em seguida, os usuários do seu código sabem exatamente quanta precisão eles realmente têm.
fonte
abs(a-b) < tiny(1._dp)
como faço acima.tiny(1._dp)=2.22507385850720138E-308
(cometi um erro no meu comentário anterior, é 2e-308, não 1e-320). Então este é o meu erro absoluto. Então eu preciso comparar o erro relativo. Entendo o seu ponto, acho que você está certo. Obrigado!