浮動小数点数のまとめ
概要
- 限られたビットの中で小数を表現する方法の一つが浮動小数点数
- 今日の実装は処理系、言語を問わずIEEE方式(IEEE 754)が標準
- とりあえずIEEE方式の倍精度浮動小数点数(C言語のdouble)の仕組みを覚えておけばいい
Wikipedia
浮動小数点数 - Wikipedia
IEEE 754 - Wikipedia
IEEE 754
IEEE 754: Standard for Binary Floating-Point Arithmetic
ビットの表現
小数を「(符号) 基数2の指数×仮数」で表現するのが基本的な考え方。
倍精度浮動小数点数の場合、64ビットを以下の3つの部分に分けて意味を持たせる。
Sign (符号部, 1bit)
- 0 なら +, 1 なら - を表す
Exponent (指数部, 11bits)
- 11ビットの符号無し整数で表現できるのは 0〜2047(=2^11-1)。
- 0(全てのビットが0)と2047(全てのビットが1)は特殊な用途(後述)で使用される。
- 1〜2046の範囲を、1023 のバイアス付き整数(ゲタ履き表現)で表す。
例えば符号無し整数の値が 1 であれば -1022、1023 であれば 0、2046 であれば 1023 を意味する。 - だいたい ±1000 と覚えておけば、ビット数を忘れても思い出しやすい
- 基数を 10 で考えると、だいたい 1e-308 〜 1e+308 の範囲を表せる
Significand (仮数部, 52bits)
- 指数部を掛け合わせる前の絶対値
- 整数部分が 1 になるように指数部を調整する(正規化)ので、整数部分の1は明らかであり表現しない。(hidden bitと呼ばれる)
- 例えば仮数が10進表記で 1.75 である場合は、2進表記だと 1.11。
整数部分を除外して、仮数部の表現は 11000000...(以下0が続く) となる。 - 0付近のごく小さい数を表すために、非正規化表現も用意されている。
(アンダーフローギャップを埋めるための重要な仕組み)
表現の種類
IEEE 754 では5種類の表現が定義されている。
s = sign (0 or 1)
q = exponent (指数部の符号無し整数表記)
c = significand (仮数部の符号無し整数表記) <=小数ではなく整数として見た場合の値
としたときの計算式と合わせて書くと以下のようになる。
種類 exponent(q) significand(c) 表している値 ゼロ 0 0 +0, -0 (符号の区別がある) 非正規化数 0 1〜 正規化数 1〜2046 0〜 無限大 2047 0 NaN(非数) 2047 1〜 基本的に符号の区別はない (詳細はWikipedia参照)
実装の確認
C言語でも可能だが、手っ取り早く Python で確認してみた。
(PyPIからbitarrayをインストールする)
出力例
sign double : v <exponent > < significant > ====================================================================================== 0.0: 0 00000000000 0000000000000000000000000000000000000000000000000000 -0.0: 1 00000000000 0000000000000000000000000000000000000000000000000000 2.0: 0 10000000000 0000000000000000000000000000000000000000000000000000 3.0: 0 10000000000 1000000000000000000000000000000000000000000000000000 1.0: 0 01111111111 0000000000000000000000000000000000000000000000000000 0.1: 0 01111111011 1001100110011001100110011001100110011001100110011010 nan: 0 11111111111 1000000000000000000000000000000000000000000000000000 inf: 0 11111111111 0000000000000000000000000000000000000000000000000000 -inf: 1 11111111111 0000000000000000000000000000000000000000000000000000 2.22507385851e-308: 0 00000000001 0000000000000000000000000000000000000000000000000001 1.79769313486e+308: 0 11111111110 1111111111111111111111111111111111111111111111111111 2.22507385851e-308: 0 00000000000 1111111111111111111111111111111111111111111111111111 4.94065645841e-324: 0 00000000000 0000000000000000000000000000000000000000000000000001