機械イプシロン
定義と性質
機械イプシロン (machine epsilon) は浮動小数点演算の丸めによって発生する相対誤差の上限を意味する。これはコンピュータ演算を使った数値解析特有のトピックである。macheps, unit roundoff または計算機イプシロンとも呼ばれ記号 \(\epsilon\), \({\bf u}\) で表される。
数値解析において、機械イプシロンはしばしば値 \(x\) に対して同じ次元の別の値 \(\delta\) が十分無視できる大きさかを判断する目的で使用される。具体的には、演算上の丸めにより \(x+\delta\to x\) となれば \(\delta\) は \(x\) に比べて無視できると考えて良い。これは特に、近似値を求める上で値が十分に収束したかの判断となる。
一般的な言語で使用されている IEEE 754 定義の浮動小数点の機械イプシロンは以下の通り。ある値 \(x\) に対して \(\delta \lt 2\epsilon |x|\) であれば \(\delta\) は \(x\) に対して無視できると言える。
精度 | 仮数bit | \(\epsilon=b^{1-p}/2\) | \(2\epsilon=b^{1-p}\) | 言語 |
---|---|---|---|---|
binary16 | 10+1bit | 0.0004883 | 0.000977 | Float16 (Julia) |
binary32 | 23+1bit | 5.9604645e-8 | 1.1920929e-7 | Float |
binary64 | 52+1bit | 1.1102230246251565e-16 | 2.220446049250313e-16 | Double |
算出方法
浮動小数点の仮数部を一般化して考えてみよう。仮数部の数値表現 \(x\) は基数 \(b\) と精度 \(p\) (基数 \(b\) の有効数字)、基数が配置されている位置 (桁) すなはち指数 \(e\) で表すことができる。ここで \(0\leq x\lt b\) である。\[ x = [d_{p-1}.d_{p-2}\cdots d_1 d_0] = \sum_{e=0}^{p-1} d_e \times b^{e-(p-1)} \] 例えば \(b=\)10 進数で有効数字 \(p=\)3 桁の \(x=\)3.14 は以下のように表される。\[ 3.14 = [3, 1, 4] = 3 \times 10^0 + 1 \times 10^{-1} + 4 \times 10^{-2} \] 各桁の表記はそれぞれ \(\epsilon_e \lt b^{e-(p-1)}\) の相対誤差を含んでいることが分かるだろう。
機械イプシロンは相対誤差の限界であるため指数 \(e=0\) のみを考慮すればよい。一般的に用いられている "最も近い方に寄せる" 丸め実装であれば誤差は最大でも \(\pm\epsilon=\pm\frac{b^{1-p}}{2}\) の範囲をとる。仮数部の最大桁(最左桁)が1の位を示すことを考えれば、この相対誤差は最大桁の最小値 1 に対する丸めで起きうる最大誤差を意味している。
以上より、浮動小数点 \(x\) と隣接の浮動小数点との間の最大間隔は \(2 \epsilon \times |x|\) と表すことができる。
なお \(2\epsilon=b^{1-p}\) を機械イプシロン \(\epsilon\) とする認識も一般に多くどちらも本質的に間違いではないことに注意。ここでは LAPACK に従っている。
言語機能
C/C++
float.h
に各精度の機械イプシロン \(2\epsilon\) が定義されている。long double
は有効数字 64bit の拡張倍精度浮動小数点である。
#include <float.h>
printf("%.7e\n", FLT_EPSILON);
printf("%.15e\n", DBL_EPSILON);
printf("%.19Le\n", LDBL_EPSILON);
1.1920929e-07
2.220446049250313e-16
1.0842021724855044340e-19
Julia
Julia は標準機能として各浮動小数点精度の機械イプシロン \(2\epsilon\) を求める関数 eps()
が用意されている。
julia> eps(Float16)
Float16(0.000977)
julia> eps(Float32)
1.1920929f-7
julia> eps(Float64)
2.220446049250313e-16
Java/Scala/C#
浮動小数点で表すことのできる最小の正の非正規化値が定義されているがこれは機械イプシロンではないため独自に定義する必要がある。Java/Scala ではべき乗演算による算出でも精度の範囲で差異はでないためリテラル化する必要はない。他は要確認。
scala> math.pow(2, -23).toFloat
res11: Float = 1.1920929E-7
scala> math.pow(2, -52)
res12: Double = 2.220446049250313E-16