将PI定义为什么的动机是什么?
PI=4.D0*DATAN(1.D0)
在Fortran 77代码内部?我知道它是如何工作的,但是推理是什么?
这种样式确保在将值分配给PI时,使用了任何架构可用的最大精度。
因为Fortran没有内置常量PI
。但是,与其手动输入数字并可能出现错误或不能在给定实现上获得最大可能精度,让库为您计算结果可以保证不会发生这些缺点。
以下代码也是等价的,有时你也会看到它们:
PI=DACOS(-1.D0)
PI=2.D0*DASIN(1.D0)
我认为这是有关圆周率的最短级数,因此也是最准确的。
Gregory-Leibniz级数(4/1 - 4/3 + 4/5 - 4/7...)等于pi。
atan(x) = x^1/1 - x^3/3 + x^5/5 - x^7/7...
所以,atan(1) = 1/1 - 1/3 + 1/5 - 1/7 + 1/9... 4 * atan(1) = 4/1 - 4/3 + 4/5 - 4/7 + 4/9...
那就等于Gregory-Leibniz级数,因此约等于pi 3.1415926535 8979323846 2643383279 5028841971 69399373510。
使用atan另一种找到pi的方法是:
pi = 16*atan(1/5) - 4*atan(1/239),但我认为这更复杂。
希望能对你有所帮助!
(老实说,我认为Gregory-Leibniz级数是基于atan而不是基于Gregory-Leibniz级数基于4*atan(1)。换句话说,真正的证明是:
sin^2 x + cos^2 x = 1 [定理]
如果x = pi/4 弧度,则sin^2 x = cos^2 x,或sin^2 x = cos^2 x = 1/2。
请不要给我留下评论-我仍然是未成年人。
pi = 16*atan(1/5) - 4*atan(1/239)
并不是一个数值精确的好选择。无论是 1/5
还是 1/239
都无法用浮点数精确表示。因此,由于这些浮点数的近似,atan 已经引入了误差。 - kvantour这个问题比表面上看到的更加复杂。为什么是4 arctan(1)
?为什么不是其他表示,例如3 arccos(1/2)
?
通过排除法来寻找答案。
数学介绍:当使用反三角函数如arccos, arcsin和arctan时,可以通过多种方式轻松计算π:
π = 4 arctan(1) = arccos(-1) = 2 arcsin(1) = 3 arccos(1/2) = 6 arcsin(1/2)
= 3 arcsin(sqrt(3)/2) = 4 arcsin(sqrt(2)/2) = ...
这里还有许多其他三角函数值的精确代数表达式,可以在这里使用。
浮点参数1:众所周知,有限的二进制浮点表示不能表示所有实数。一些这样的数字例子是1/3, 0.97, π, sqrt(2), ...
。因此,我们应该排除任何计算π的数学计算,其中反三角函数的参数无法以数字方式表示。这使得我们只剩下参数-1,-1/2,0,1/2
和1
。
π = 4 arctan(1) = 2 arcsin(1)
= 3 arccos(1/2) = 6 arcsin(1/2)
= 2 arccos(0)
= 3/2 arccos(-1/2) = -6 arcsin(-1/2)
= -4 arctan(-1) = arccos(-1) = -2 arcsin(-1)
浮点数参数2:在二进制表示中,一个数字可以表示为0.bnbn-1...b0 x 2m。如果反三角函数得出其参数的最佳数字二进制近似值,我们不希望通过乘法失去精度。为此,我们只应该使用2的幂进行乘法运算。π = 4 arctan(1) = 2 arcsin(1)
= 2 arccos(0)
= -4 arctan(-1) = arccos(-1) = -2 arcsin(-1)
注意: 这在 IEEE-754二进制64位 表示中可见(这是最常见的DOUBLE PRECISION
或kind=REAL64
形式)。 在那里我们有
write(*,'(F26.20)') 4.0d0*atan(1.0d0) -> " 3.14159265358979311600"
write(*,'(F26.20)') 3.0d0*acos(0.5d0) -> " 3.14159265358979356009"
REAL
或kind=REAL32
形式)和IEEE-754二进制128(最常见的kind=REAL128
形式)中不存在。
实现参数:在英特尔CPU上,atan2
是x86指令集中的一部分,作为FPATAN
,而其他反三角函数则来源于atan2
。可能的推导如下: mathematically numerically
ACOS(x) = ATAN2(SQRT(1-x*x),1) = ATAN2(SQRT((1+x)*(1-x)),1)
ASIN(x) = ATAN2(1,SQRT(1-x*x)) = ATAN2(1,SQRT((1+x)*(1-x)))
π = 4 arctan(1)
注意: 这是一个模糊的论点。我相信有更好的意见。
FPATAN
有趣的阅读资料: 如何实现反正切函数?, x87三角函数指令
Fortran 论点: 为什么我们应该将 π
近似为:
integer, parameter :: sp = selected_real_kind(6, 37)
integer, parameter :: dp = selected_real_kind(15, 307)
integer, parameter :: qp = selected_real_kind(33, 4931)
real(kind=sp), parameter :: pi_sp = 4.0_sp*atan2(1.0_sp,1.0_sp)
real(kind=dp), parameter :: pi_dp = 4.0_dp*atan2(1.0_dp,1.0_dp)
real(kind=qp), parameter :: pi_qp = 4.0_qp*atan2(1.0_qp,1.0_qp)
而不是:
real(kind=sp), parameter :: pi_sp = 3.14159265358979323846264338327950288_sp
real(kind=dp), parameter :: pi_dp = 3.14159265358979323846264338327950288_dp
real(kind=qp), parameter :: pi_qp = 3.14159265358979323846264338327950288_qp
REAL
应该表示IEEE-754浮点数。 REAL
的表示是处理器相关的。这意味着我可以查询selected_real_kind(33, 4931)
并期望获得一个二进制128位浮点数,但我可能会得到一个代表更高精度浮点的kind
。也许是100位数字,谁知道呢。在这种情况下,我的上面的数字串就太短了!难道我们不能使用这个来确保吗?即使那个文件也可能太短!sin(pi)从不为零
。write(*,'(F17.11)') sin(pi_sp) => " -0.00000008742"
write(*,'(F26.20)') sin(pi_dp) => " 0.00000000000000012246"
write(*,'(F44.38)') sin(pi_qp) => " 0.00000000000000000000000000000000008672"
这被理解为:
pi = 4 ATAN2(1,1) = π + δ
SIN(pi) = SIN(pi - π) = SIN(δ) ≈ δ
program print_pi
! use iso_fortran_env, sp=>real32, dp=>real64, qp=>real128
integer, parameter :: sp = selected_real_kind(6, 37)
integer, parameter :: dp = selected_real_kind(15, 307)
integer, parameter :: qp = selected_real_kind(33, 4931)
real(kind=sp), parameter :: pi_sp = 3.14159265358979323846264338327950288_sp
real(kind=dp), parameter :: pi_dp = 3.14159265358979323846264338327950288_dp
real(kind=qp), parameter :: pi_qp = 3.14159265358979323846264338327950288_qp
write(*,'("SP "A17)') "3.14159265358..."
write(*,'(F17.11)') pi_sp
write(*,'(F17.11)') acos(-1.0_sp)
write(*,'(F17.11)') 2.0_sp*asin( 1.0_sp)
write(*,'(F17.11)') 4.0_sp*atan2(1.0_sp,1.0_sp)
write(*,'(F17.11)') 3.0_sp*acos(0.5_sp)
write(*,'(F17.11)') 6.0_sp*asin(0.5_sp)
write(*,'("DP "A26)') "3.14159265358979323846..."
write(*,'(F26.20)') pi_dp
write(*,'(F26.20)') acos(-1.0_dp)
write(*,'(F26.20)') 2.0_dp*asin( 1.0_dp)
write(*,'(F26.20)') 4.0_dp*atan2(1.0_dp,1.0_dp)
write(*,'(F26.20)') 3.0_dp*acos(0.5_dp)
write(*,'(F26.20)') 6.0_dp*asin(0.5_dp)
write(*,'("QP "A44)') "3.14159265358979323846264338327950288419..."
write(*,'(F44.38)') pi_qp
write(*,'(F44.38)') acos(-1.0_qp)
write(*,'(F44.38)') 2.0_qp*asin( 1.0_qp)
write(*,'(F44.38)') 4.0_qp*atan2(1.0_qp,1.0_qp)
write(*,'(F44.38)') 3.0_qp*acos(0.5_qp)
write(*,'(F44.38)') 6.0_qp*asin(0.5_qp)
write(*,'(F17.11)') sin(pi_sp)
write(*,'(F26.20)') sin(pi_dp)
write(*,'(F44.38)') sin(pi_qp)
end program print_pi
这是一种计算任意精度 pi
的精确方法。您可以继续执行该函数以获得更高的精度,并在任何时候停止以获得近似值。
相比之下,将pi
指定为常量只会提供与最初给定的精度完全相同的精度,这可能不适用于高度科学或数学应用(如经常使用的Fortran)。
那听起来非常像是对编译器漏洞的解决方法。或者这个特定程序依赖于该身份信息的准确性,因此程序员做了保证。
DATAN()
函数:根据参数,ATAN()
函数会自动别名为其相应的单精度和双精度版本。 - jvriesem