浮動小数点精度(REAL vs DOUBLE PRECISION)
『FORTRAN』では浮動小数点数は IEEE 754 規格に基づいて表現されます。REAL(単精度・32 ビット)と DOUBLE PRECISION(倍精度・64 ビット)では表現できる数値の範囲と精度が異なり、演算を重ねるほど丸め誤差が蓄積します。数値計算プログラムを書く際は精度の限界を把握し、適切な種別(kind)を選択することが重要です。
構文
! ----------------------------------------------- ! 浮動小数点の種別(kind)を明示的に指定する方法 ! ----------------------------------------------- ! 単精度(32 ビット): 有効桁数 約 7 桁 real(kind=4) :: x_single ! 倍精度(64 ビット): 有効桁数 約 15〜16 桁 real(kind=8) :: x_double ! 移植性の高い書き方: selected_real_kind で種別を取得 integer, parameter :: SP = selected_real_kind(6, 37) ! 単精度相当 integer, parameter :: DP = selected_real_kind(15, 307) ! 倍精度相当 real(SP) :: a real(DP) :: b ! リテラルに種別を付ける(省略すると単精度になるので注意) a = 1.23456_SP b = 1.23456789012345_DP ! ----------------------------------------------- ! 精度・範囲に関する組み込み関数 ! ----------------------------------------------- ! 有効桁数(十進)を返す print *, precision(a) ! 単精度: 6 print *, precision(b) ! 倍精度: 15 ! 最大指数(十進)を返す print *, range(a) ! 単精度: 37 print *, range(b) ! 倍精度: 307 ! 機械イプシロン(1.0 に足したとき 1.0 と区別できる最小値) print *, epsilon(a) ! 単精度: 約 1.19E-7 print *, epsilon(b) ! 倍精度: 約 2.22E-16
構文一覧
| 構文 / 関数 | 概要 |
|---|---|
real(kind=4) | 単精度浮動小数点(32 ビット)を宣言します。有効桁数は約 7 桁です。 |
real(kind=8) | 倍精度浮動小数点(64 ビット)を宣言します。有効桁数は約 15〜16 桁です。 |
selected_real_kind(p, r) | 十進有効桁数 p 以上・指数範囲 r 以上を満たす最小の kind 値を返します。移植性の高いコードを書く際に使います。 |
precision(x) | 変数 x の十進有効桁数を返します。 |
range(x) | 変数 x が表現できる十進指数の最大値を返します。 |
epsilon(x) | 1.0 + epsilon(x) > 1.0 となる最小の正の実数(機械イプシロン)を返します。 |
tiny(x) | x の型で表現できる最小の正の正規化数を返します。 |
huge(x) | x の型で表現できる最大値を返します。 |
nearest(x, s) | s の符号方向で x に最も近い機械数を返します。丸め誤差の解析に使います。 |
サンプルコード
eva_precision.f90
! eva_precision.f90 — 浮動小数点の精度と丸め誤差を確認するサンプルです
! エヴァンゲリオンの登場人物の使徒撃破数を使って演算誤差を示します
!
! コンパイルと実行:
! gfortran eva_precision.f90 -o eva_precision && ./eva_precision
program eva_precision
implicit none
! -----------------------------------------------
! 移植性の高い種別定数を定義します
! -----------------------------------------------
integer, parameter :: SP = selected_real_kind(6, 37) ! 単精度相当
integer, parameter :: DP = selected_real_kind(15, 307) ! 倍精度相当
real(SP) :: sp_val, sp_sum
real(DP) :: dp_val, dp_sum
integer :: i
! -----------------------------------------------
! 精度情報を表示します
! -----------------------------------------------
print *, "===== 浮動小数点の精度情報 ====="
print *, ""
print "(a, i3)", " 単精度 有効桁数 : ", precision(sp_val)
print "(a, i5)", " 単精度 指数範囲 : ", range(sp_val)
print "(a, es12.4)", " 単精度 機械イプシロン: ", epsilon(sp_val)
print *, ""
print "(a, i3)", " 倍精度 有効桁数 : ", precision(dp_val)
print "(a, i5)", " 倍精度 指数範囲 : ", range(dp_val)
print "(a, es20.12)", " 倍精度 機械イプシロン: ", epsilon(dp_val)
print *, ""
! -----------------------------------------------
! 丸め誤差の蓄積を示します
! 0.1 を 10 回足すと 1.0 になるはずですが誤差が出ます
! -----------------------------------------------
sp_sum = 0.0_SP
dp_sum = 0.0_DP
do i = 1, 10
sp_sum = sp_sum + 0.1_SP
dp_sum = dp_sum + 0.1_DP
end do
print *, "===== 0.1 を 10 回加算した結果(理論値: 1.0) ====="
print *, ""
print "(a, f20.17)", " 単精度の結果: ", sp_sum
print "(a, f20.17)", " 倍精度の結果: ", dp_sum
print "(a, f20.17)", " 真の値 : ", 1.0_DP
print *, ""
! -----------------------------------------------
! 碇シンジの使徒撃破ログ(累積ダメージ計算)
! 単精度と倍精度の差が顕在化するケースです
! -----------------------------------------------
print *, "===== 碇シンジ 累積ダメージ計算(使徒撃破ログ) ====="
print *, ""
! 単精度で大きな数と小さな数を足すと情報が失われます
sp_val = 1000000.0_SP + 0.001_SP - 1000000.0_SP
dp_val = 1000000.0_DP + 0.001_DP - 1000000.0_DP
print "(a, f12.6)", " 単精度 (1000000 + 0.001 - 1000000): ", sp_val
print "(a, f12.6)", " 倍精度 (1000000 + 0.001 - 1000000): ", dp_val
print "(a, f12.6)", " 理論値 : ", 0.001_DP
print *, ""
! -----------------------------------------------
! 碇ゲンドウの評価(0.1 の二進表現の確認)
! -----------------------------------------------
print *, "===== 0.1 は二進数で正確に表現できません ====="
print *, ""
print "(a, f25.20)", " 単精度 0.1 の実際の値: ", real(0.1_SP, DP)
print "(a, f25.20)", " 倍精度 0.1 の実際の値: ", 0.1_DP
print *, ""
print *, " 精度の高い科学計算には倍精度(DP)の使用を推奨します。"
end program eva_precision
gfortran eva_precision.f90 -o eva_precision && ./eva_precision ===== 浮動小数点の精度情報 ===== 単精度 有効桁数 : 6 単精度 指数範囲 : 37 単精度 機械イプシロン: 1.1921E-07 倍精度 有効桁数 : 15 倍精度 指数範囲 : 307 倍精度 機械イプシロン: 2.220446049250E-16 ===== 0.1 を 10 回加算した結果(理論値: 1.0) ===== 単精度の結果: 1.00000011920929 倍精度の結果: 0.99999999999999989 真の値 : 1.00000000000000000 ===== 碇シンジ 累積ダメージ計算(使徒撃破ログ) ===== 単精度 (1000000 + 0.001 - 1000000): 0.000000 倍精度 (1000000 + 0.001 - 1000000): 0.001000 理論値 : 0.001000 ===== 0.1 は二進数で正確に表現できません ===== 単精度 0.1 の実際の値: 0.10000000149011612 倍精度 0.1 の実際の値: 0.10000000000000001 精度の高い科学計算には倍精度(DP)の使用を推奨します。
概要
IEEE 754 規格では浮動小数点数を符号・指数部・仮数部の三つの領域に分けて二進数で表現します。単精度(32 ビット)は有効桁数が約 7 桁、倍精度(64 ビット)は約 15〜16 桁です。0.1 のように十進で有限小数でも二進では無限小数になる値は近似値として格納されるため、演算を繰り返すと誤差が蓄積します。epsilon() で取得できる機械イプシロンは「その型で 1.0 に足したとき 1.0 と区別できる最小の数」であり、アルゴリズムの収束判定に利用できます。精度を明示するには selected_real_kind() で kind 定数を定義し、リテラルにも 1.0_DP のように種別を付けることが重要です。kind を省略したリテラルは単精度と見なされるため、倍精度変数への代入時に精度が失われることがあります。より高度な IEEE 演算(NaN・Inf の判定など)については IEEE_ARITHMETIC モジュール を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。