Como esses valores de precisão dupla são precisos com 20 decimais?
Estou testando alguns erros de equivalência muito simples quando a precisão é um problema e esperava executar as operações com precisão dupla estendida (para que eu soubesse qual seria a resposta em ~ 19 dígitos) e, em seguida, execute as mesmas operações com precisão dupla (onde haveria erro de arredondamento no décimo sexto dígito), mas, de alguma forma, minha aritmética de dupla precisão está mantendo 19 dígitos de precisão.
Quando executo as operações em duplo estendido, depois codifico os números em outra rotina do Fortran, recebo os erros esperados, mas há algo estranho acontecendo quando atribuo uma variável de precisão dupla estendida a uma variável de precisão dupla aqui?
program code_gen
implicit none
integer, parameter :: Edp = selected_real_kind(17)
integer, parameter :: dp = selected_real_kind(8)
real(kind=Edp) :: alpha10, x10, y10, z10
real(kind=dp) :: alpha8, x8, y8, z8
real(kind = dp) :: pi_dp = 3.1415926535897932384626433832795028841971693993751058209749445
integer :: iter
integer :: niters = 10
print*, 'tiny(x10) = ', tiny(x10)
print*, 'tiny(x8) = ', tiny(x8)
print*, 'epsilon(x10) = ', epsilon(x10)
print*, 'epsilon(x8) = ', epsilon(x8)
do iter = 1,niters
x10 = rand()
y10 = rand()
z10 = rand()
alpha10 = x10*(y10+z10)
x8 = x10
x8 = x8 - pi_dp
x8 = x8 + pi_dp
y8 = y10
y8 = y8 - pi_dp
y8 = y8 + pi_dp
z8 = z10
z8 = z8 - pi_dp
z8 = z8 + pi_dp
alpha8 = alpha10
write(*, '(a, es30.20)') 'alpha8 .... ', x8*(y8+z8)
write(*, '(a, es30.20)') 'alpha10 ... ', alpha10
if( alpha8 .gt. x8*(y8+z8) ) then
write(*, '(a)') 'ERROR(.gt.)'
elseif( alpha8 .lt. x8*(y8+z8) ) then
write(*, '(a)') 'ERROR(.lt.)'
endif
enddo
end program code_gen
Onderand()
é a função gfortran encontradaaqui.
Se falamos apenas de um tipo de precisão (por exemplo, duplo), podemos denotar a máquina epsilon comoE16
que é aproximadamente2.22E-16
. Se fizermos uma simples adição de dois números reais,x+y
, o número expresso da máquina resultante é(x+y)*(1+d1)
Ondeabs(d1) < E16
. Da mesma forma, se multiplicarmos esse número porz
, o valor resultante é realmente(z*((x+y)*(1+d1))*(1+d2))
que é quase(z*(x+y)*(1+d1+d2))
Ondeabs(d1+d2) < 2*E16
. Se passarmos agora para uma precisão dupla estendida, a única coisa que muda é queE16
vira paraE20
e tem um valor em torno de1.08E-19
.
Minha esperança era realizar a análise com precisão dupla estendida para que eu pudesse comparar dois números quedevemos seja igual, mas mostre que, ocasionalmente, o erro de arredondamento fará com que as comparações falhem. Atribuindox8=x10
, Eu esperava criar uma 'versão' de precisão dupla do valor estendido de precisão duplax10
, onde apenas os primeiros ~ 16 dígitos dex8
conforme os valores dex10
, mas, ao imprimir os valores, mostra que todos os 20 dígitos são iguais e que o erro de arredondamento de precisão dupla esperado não está ocorrendo como seria de esperar.
Note-se também que, antes dessa tentativa, eu escrevi um programa que realmente escreveoutro programa em que os valores dex
, y
ez
são 'codificados' com 20 casas decimais. Nesta versão do programa, as comparações de.gt.
e.lt.
falhou conforme o esperado, mas não consigo duplicar as mesmas falhas lançando um valor estendido de precisão dupla como uma variável de precisão dupla.
Em uma tentativa de "perturbar" ainda mais os valores de precisão dupla e adicionar um erro de arredondamento, adicionei e subtraímos,pi
das minhas variáveis de precisão dupla, que devem deixar as variáveis restantes com algum erro de arredondamento de precisão dupla, mas ainda não estou vendo isso no resultado final.