考虑代码,
PROGRAM TRIG_TEST
IMPLICIT NONE
DOUBLE PRECISION, PARAMETER :: PI=4.D0*DATAN(1.0D)
print *, sin(PI/2.0), cos(PI/2.0)
END PROGRAM TRIG_TEST
使用gfortran
输出进行编译,
1.0000000000000000 6.1232339957367660E-017
我知道通常的浮点问题但是sin
函数是否相同为1,但cos
函数不一样为零?
答案 0 :(得分:4)
以下假设double
是IEEE 754基本64位二进制格式。三角函数例程的常见实现不如格式要求准确。但是,对于这个答案,让我们假设它们可以返回最准确的结果。
π无法在double
中准确表示。最接近的可能值是884279719003555/281474976710656或3.141592653589793115997963468544185161590576171875。我们称之为 p 。
p / 2的正弦值约为1 - 1.8747•10 -33 。在double
中可以表示的两个值是1和0.99999999999999988897769753748434595763683319091796875,其大约是1 - 1.11•10 -16 。它们中的较近者是1,因此p/2
的正弦值的最接近的可表示值恰好为1.
p / 2的余弦值约为6.123233995736765886•10 -17 。在double
中可以表示的最接近的值是6.12323399573676603586882014729198302312846062338790031898128063403419218957424163818359375•10 -17 。
因此,您观察到的结果是与真实数学值最接近的可能结果。
答案 1 :(得分:1)
让我们看看为什么sin
的结果给出了1.在您的代码中
DOUBLE PRECISION, PARAMETER :: PI=4.D0*DATAN(1.0D0)
这是pi值的浮点近似值。它可能非常接近实际价值,但它并非如此。此外,sin
函数在评估罪时有错误。我们希望这些都是小错误。
我们期望cos(pi / 2)的值为零。您的浮点计算cos(PI/2)
在数学答案中的误差约为6.1232339957367660E-017
。让我们假设你的罪计算具有相似的误差幅度。
现在看看epsilon(0d)
的价值。这是1d0+epsilon(0d0)
不等于1d0
的最小数字。假设误差远小于此数字,在模型中(您报告" ~2.2e-16")。
因此,1
是与浮点计算的实际值最接近的可表示数字。
考虑一下程序
use, intrinsic :: iso_fortran_env, only : real128, real64
implicit none
real(real64), parameter :: PI=4.D0*ATAN(1._real64)
real(real128), parameter :: PI_approx = PI ! Not 4*ATAN(1._real128)
print *, SIN(PI/2), COS(PI/2)
print *, SIN(PI_approx/2), COS(PI_approx/2)
end
这计算(可能)你PI/2
的罪,但精度更高(使用相同的pi近似值)。我的编译器在第二种情况下报告的值与1
不同,但差异远小于epsilon(0._real64)
。
作为样式点,通常最好避免datan
并使用通用atan
。 double precision
也可以由适当的种类参数替换。这些都显示在我上面的程序中。