PI を次のように定義する動機は何ですか
PI=4.D0*DATAN(1.D0)
Fortran 77 コード内? 仕組みは理解できたのですが、その理由は何ですか?
このスタイルにより、値を PI に割り当てるときに、任意のアーキテクチャで利用可能な最大精度が使用されるようになります。
Fortran には の組み込み定数がないためPI
です。ただし、数値を手動で入力して間違いを犯したり、指定された実装で可能な最大精度を得られなかったりするのではなく、ライブラリに結果を計算させることで、これらの欠点のいずれも発生しないことが保証されます。
これらは同等であり、時々それらも表示されます。
PI=DACOS(-1.D0)
PI=2.D0*DASIN(1.D0)
これは、pi
任意の精度で計算するための正確な方法だからです。関数の実行を継続して精度を高め、任意の時点で停止して近似値を得ることができます。
対照的にpi
、定数として指定すると、最初に与えられた精度とまったく同じ精度が得られますが、高度な科学的または数学的なアプリケーションには適していない可能性があります (Fortran は頻繁に使用されます)。
この質問には、目に見える以上のものがあります。なぜ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:有限の 2 進浮動小数点表現がすべての実数を表すことができないことはよく理解されています。そのような数の例は次のとおり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.b n b n-1 ...b 0 x 2 mとして表されます。逆三角関数がその引数に最適な数値バイナリ近似を思いついた場合、乗算によって精度を失うことは望ましくありません。この目的のためには、2 のべき乗のみを乗算する必要があります。
π = 4 arctan(1) = 2 arcsin(1)
= 2 arccos(0)
= -4 arctan(-1) = arccos(-1) = -2 arcsin(-1)
注:これは、IEEE-754 binary64DOUBLE PRECISION
表現 (またはの最も一般的な形式kind=REAL64
) で表示されます。そこにある
write(*,'(F26.20)') 4.0d0*atan(1.0d0) -> " 3.14159265358979311600"
write(*,'(F26.20)') 3.0d0*acos(0.5d0) -> " 3.14159265358979356009"
この違いは、IEEE-754 binary32REAL
(またはの最も一般的な形式kind=REAL32
) とIEEE-754 binary128 ( の最も一般的な形式kind=REAL128
)にはありません。
実装の引数:インテル CPU では、はx86 命令セットatan2
の一部であり、他の逆三角関数は から派生しています。潜在的な派生物は次のとおりです。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
: arctanはどのように実装されていますか?、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
その答えは、Fortran 標準にあります。標準では、どのような種類の もIEEE-754 浮動小数点数を表す必要があるとは決して述べていません。の表現はプロセッサに依存します。これは、照会してbinary128 浮動小数点数を取得できることを意味しますが、はるかに高い精度で浮動小数点を表す戻り値を取得する可能性があります。たぶん100桁、誰が知っていますか。この場合、上記の数字の文字列は短すぎます! 念のためにこれを使用することはできませんか?そのファイルでさえ短すぎる可能性があります。REAL
REAL
selected_real_kind(33, 4931)
kind
興味深い事実 :sin(pi) is never zero
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
これは、コンパイラのバグの回避策のように思えます。または、この特定のプログラムはその ID が正確であることに依存しているため、プログラマーがそれを保証した可能性があります。