浮動小数点数とIEEE754
演算誤差を考えるために、コンピュータの小数計算の理解を深めたい。
IEEE754
計算機で小数を扱う方法はいろいろあるが、標準仕様としてIEEE754が広く受け入れられている。
IEEE754では単精度浮動小数点数を32bit、倍精度浮動小数点数を64bitで表現する。説明の便宜上、前者をfloat・後者をdoubleと呼ぶことにする。浮動小数点数の話は特定の言語に限定されるものではないのだが、その方が読みやすいと思うので許してほしい。また話を簡単にするために、ここでは浮動小数点数=floatを前提にして話を進める。
小数の指数表現
どのような小数(実数)でも、指数表現を使えば簡潔に書ける。
IEEE754の浮動小数点数は2進数の指数表現を使って任意の小数を表す。IEEE754の仕様では、floatは23bitの仮数部と8bitの指数部を、doubleは53bitの仮数部と11bitの指数部を持つ。
ここまではよく聞く話だが、実際にどのような値が格納されるかまでは意識する機会がない。
例えば仮数部は、「整数部が1であるような2進小数の小数部分」である。これはどういうことだろうか。
2進小数の指数表現
10進数を2進数に変換するとき、通常は
同様に、
10進数を2進数に変換するには以下の計算を行えばよい。
- 整数部を2で割り続けて、余りが出れば対応する位は1
- 小数部に2をかけ続けて、1を超えれば対応する位は1
やってみればどうということはない。
例えば
これを指数表記に直せば、
となる。下線部が冒頭で言った「整数部が1であるような2進小数の小数部分」であり、ここから23bitが仮数部に保存される。
IEEE754と指数部
他方、2進小数を指数表記したときの指数が指数部に保存される。floatの指数部は8bitあるので-128~127を指定できる・・という訳ではないようだ。
IEEE754によると、指数部で表現できる値は-126~127である。-128, -127は特殊な状態を表すために犠牲になった。また、bit表現は指数の値に127(0b01111111)を加算した値とするようだ
- -128 (0b10000000) + 127(0b01111111) = -1(0b11111111) ... NaN, Inf
- -127 (0b10000001) + 127(0b01111111) = 0(0b00000000) ... 非正規化数, 0
非正規化数については後述。ようは指数部の限界を超えた小さい数である。
指数部がオール0またはオール1でなければ正常(正規化数)だと判断できる。
表現できる数字の限界
float/doubleは仮数部がせいぜい23/53bitしかないので、表現できない数字が出てくる。floatの有効桁数は2進小数の小数部23bitと暗黙の1bitの整数部を含めた24桁である。
いろいろなビット配列を考えながら、表現できる数字の限界を探ってみよう。
大きな数字
例えば
次の小数は2進数で有効数字が25桁ある。24bitを超えた分の数字は丸められる。
以降、桁数が増えるにつれて誤差も大きくなる。
これは浮動小数点数の性質として知られている。浮動小数点数が表現できる数字は離散的で、その幅は0から離れるほど大きくなる。
floatの指数部は最大127まで指定できる。しかし仮数部の整数部は1と決められているため、どう足掻いても
指数表記に直すと3.40283466E+38で、C言語ではFLT_MAXとして定義されている。
小さな数字
大きな数字の次は小さな数字に着目したい。整数と違い、2進小数は10進小数をはなから正確に表現することができない。従って10進小数の0.1は、0.1に最も近い2進小数として表される。
手始めに整数部が1の2進小数を考えよう。このとき仮数部の暗黙の1は
仮数部のbitが全て0ならば、二進小数は整数の1を表す。では、この次に大きい浮動小数点数は何だろうか。
結論を言うと、それは仮数部の最小bitだけが1の浮動小数点数である。
この数字は1よりもFLT_EPSILONとして定義されている。
計算機イプシロンの説明として「1よりも大きな最小の値と1との差」と言われても正直な所、あまりピンと来ない。上に示したように、浮動小数点数の仮数部の最小bitを1大きくしたときの数字の変化量だと思えばよい。ただし実際の変化量は指数部によって大きくも小さくもなる。そこで、指数部が0のときの変化量を計算機イプシロンとして定義している。計算機イプシロンは誤差の議論をするときにまた詳しく説明したい。
一旦計算機イプシロンの話は忘れて、より小さな数字を探しに行こう。float型の指数部の最小値は-126なので、仮数部が全て0で指数部が-126の二進小数
が一番小さそうだ。これをは10進数で1.17549E-38であり、C言語ではFLT_MINとして定義されている。
非正規化数
実は非正規化数を使うと、より小さな数字を表現できる。
指数部の全てのbitが0のとき、浮動小数点数は非正規化数を表す。仮数部は「整数部を0とし、指数部を
正規化数では0を表すことができない。言われてみればそうだなーと。
例えば
0でない最小の非正規化数は
であり、FLT_TRUE_MINとして定義されている。
まとめ
IEEE754の単精度浮動小数点のポイントとなる値をまとめておいた。
| ± | Exp | Exp(bits) | Mantissa(bits) | Math | Decimal | Meaning |
|---|---|---|---|---|---|---|
| 0 | 127 | 11111110 | 11111111111111111111111 | 3.4028E+38 | FLT_MAX | |
| 0 | 23 | 10010110 | 11111111111111111111111 | 16777216-1 | Max # w/ 23 s.f. | |
| 0 | 0 | 01111111 | 00000000000000000000001 | 1.192E-07 | FLT_EPSILON | |
| 0 | -126 | 00000001 | 00000000000000000000000 | 1.17549E-38 | FLT_MIN | |
| 0 | -127 | 00000000 | 00000000000000000000001 | 1.40125E-45 | FLT_TRUE_MIN | |
| 0 | -127 | 00000000 | 00000000000000000000000 | 0 | 0 | Zero |
| 0 | -128 | 11111111 | 00000000000000000000000 | INFINITY | ||
| 0 | -128 | 11111111 | xxxxxxxxxxxxxxxxxxxxxxx | NAN | ||
| 1 | -128 | 11111111 | xxxxxxxxxxxxxxxxxxxxxxx | -NAN | ||
| 1 | -128 | 11111111 | 00000000000000000000000 | -INFINITY | ||
| 1 | -127 | 00000000 | 00000000000000000000000 | 0 | -0 | -Zero |
| 1 | -127 | 00000000 | 00000000000000000000001 | neg. of above | -1.40125E-45 | -FLT_TRUE_MIN |
| 1 | -126 | 00000001 | 00000000000000000000000 | neg. of above | -1.17549E-38 | -FLT_MIN |
| 1 | 127 | 11111110 | 11111111111111111111111 | neg. of above | -3.4028E+38 | -FLT_MAX |
参考
-
What Every Computer Scientist Should Know About Floating-Point Arithmetic
-
Wikipedia Links
-
Useful tools