¿Cómo son estos valores de doble precisión precisos a 20 decimales?
Estoy probando algunos errores de equivalencia muy simples cuando la precisión es un problema y esperaba realizar las operaciones en precisión doble extendida (para saber cuál sería la respuesta en ~ 19 dígitos) y luego realizar las mismas operaciones en precisión doble (donde habría un error de redondeo en el decimosexto dígito), pero de alguna manera mi aritmética de doble precisión mantiene 19 dígitos de precisión.
Cuando realizo las operaciones en doble extendido, luego codifico los números en otra rutina Fortran, obtengo los errores esperados, pero ¿ocurre algo extraño cuando asigno una variable de doble precisión extendida a una variable de doble precisión aquí?
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
dónderand()
es la función gfortran encontradaaquí.
Si estamos hablando de un solo tipo de precisión (tome, por ejemplo, doble), entonces podemos denotar épsilon de máquina comoE16
que es aproximadamente2.22E-16
. Si tomamos una simple suma de dos números reales,x+y
, entonces el número expresado de la máquina resultante es(x+y)*(1+d1)
dóndeabs(d1) < E16
. Del mismo modo, si multiplicamos ese número porz
, el valor resultante es realmente(z*((x+y)*(1+d1))*(1+d2))
que es casi(z*(x+y)*(1+d1+d2))
dóndeabs(d1+d2) < 2*E16
. Si ahora pasamos a la precisión doble extendida, entonces lo único que cambia es queE16
recurre aE20
y tiene un valor de alrededor1.08E-19
.
Mi esperanza era realizar el análisis en doble precisión extendida para poder comparar dos números quedebería sea igual pero demuestre que, en ocasiones, el error de redondeo hará que las comparaciones fallen. Asignandox8=x10
, Esperaba crear una 'versión' de doble precisión del valor extendido de doble precisiónx10
, donde solo los primeros ~ 16 dígitos dex8
ajustarse a los valores dex10
, pero al imprimir los valores, muestra que los 20 dígitos son iguales y el error de redondeo de precisión doble esperado no se produce como era de esperar.
También se debe tener en cuenta que antes de este intento, escribí un programa que realmente escribeotro programa donde los valores dex
, y
yz
están 'codificados' a 20 decimales. En esta versión del programa, las comparaciones de.gt.
y.lt.
falló como se esperaba, pero no puedo duplicar los mismos fallos al convertir un valor de doble precisión extendido como una variable de doble precisión.
En un intento de 'perturbar' aún más los valores de doble precisión y agregar un error de redondeo, agregué, luego resté,pi
de mis variables de doble precisión que deberían dejar las variables restantes con algún error de redondeo de doble precisión, pero todavía no veo eso en el resultado final.