14.2.2. 浮動小数点数
10の何十乗というような桁の大きな数0や、と1の間の細かい数を統一的に表現するために浮動小数点を導入します。
2進小数 #
整数の2進表現 ( 12.3. bit列と2進数 ) で、 右端 (=小数点) から \(k\) 左の桁は \(2^{k-1}\) を表しました。 これを小数にも拡張し、 小数点から \(k\) 右の桁は \(2^{-k}\) を表すとします。 つまり、小数点から右に 1/2の桁、1/4の桁、1/8の桁…と続きます。
10進 | 2進 |
---|---|
0 | 0 |
0.5 | 0.1 |
0.25 | 0.01 |
0.125 | 0.001 |
0.0625 | 0.0001 |
0.75 | 0.11 |
数を \(2^k\) 倍することは 2進表記での小数点を \(k\) 右にずらすこと、 \((1/2)^k\) 倍することは \(k\) 左にずらすことに対応します。 2の冪乗倍が小数点位置の移動だけで簡単にできるので、 たとえば64bit 整数表現を \([0,1)\) の範囲を \(2^{-64}\) 刻みで等間隔に表現していると扱うこともできます。より広い範囲を表現するためには、刻み幅を可変にした指数表記を導入します。
0.1 は、2進数では循環小数になります。
2進表記と10進表記とで、循環小数になる数が変わることに注意してください。 ほとんどの環境で、コンピュータに 0.1 を10回足させても 1.0とは別の数になります。
指数表記 #
とても大きな数ととても0に近い数の両方を表したいときに、表現範囲を整数より増やせるでしょうか?
アイデアは、
物理や化学で用いられる Avogadro 定数を
\(6.022\ldots \cdot 10^{23} [\text{mol}^{-1}]\)
と表記をするような、指数表記です。たとえば10進数5桁分の表記場所 abcde
があるとして、これをそのまま10進数に使うと表現範囲は
\([0,99999]\)
ですが、
\(\texttt{abc}\cdot 10^{\texttt{de}}\)
と読むことにすれば、
\([0,999\cdot 10^{99}]\)
と最大値が一気に増えます。指数に使ったde
の部分に負の数を表現できるようにすれば
\([0,1]\)
の間の数を表現することもできます。
今後、 6.022 や abc
の有効数字を表現する部分を 仮数、23や de
など桁を表現する部分を 指数 と呼びます。
代償として、刻み幅が1より大きくなることに注意してください。 また、指数表記の指数部分の数を変えることは小数点の位置が移動していると見なせることから、 このような表記を浮動小数点 とも言います。
浮動小数点数 #
コンピュータが使う浮動小数点数の代表として、IEEE 754 倍精度 (64bit) を紹介します。 数 \(x\) を3つの数 \(\langle s,m,e \rangle\) に分解して表現し、以下の式で対応させます。
\[x \approx ({-1})^s \underbrace{(1+m \cdot 2^{-52})}_{\text{仮数}} \cdot 2^{\overbrace{(e-1023)}^{\text{指数}}}\]符号部 \(s\) | 指数部 \(e\) | 仮数部 \(m\) | |
---|---|---|---|
符号長 | 1 ビット | 11 ビット | 52 ビット |
\(\langle s,m,e \rangle\) は、それぞれ符号無し2進整数です。合計で64bitを使います。
- 符号部
-
\(s\)
は1bitで
0
または1
です。このとき \(({-1})^s\) はそれぞれ \(1,\,{-1}\) となるので、 \(s\) は符号に相当します - 指数部
- \(e\) は11bitで \([0,2047]\) の整数を表現可能です。 あとで紹介するように 0 と 2047 は特別扱いするので、総合して、 \(2^{(e-1023)}\in[2^{-1022},2^{1023}]\) となります。
- 仮数部
-
\(m\)
は52bitで、
\((1+m \cdot 2^{-52})\)
を仮数として使います。
指数表記では、
\(123=1.23\cdot 10^2\)
のように、仮数の整数部が1桁になるよう桁を調整します。この操作で、0でない2進数は必ず1桁目が
1
になります。このことから1.011
のような2進の仮数なら1
は省略して.011
の部分だけ表現しようという工夫がこの主旨です。1bit だけ得をするので、ケチ表現と呼ぶこともあるようです
実数 \( x \) をすべて表せるわけではなく、対応する2進数の組 \(\langle s,m,e \rangle\) が存在するごく一部だけが表現可能です。 それ以外は、近い数で近似的に表現されます。このときの誤差を、表現誤差 と言います。
0.1 は、IEEE 754 でも循環小数となり、表現誤差を伴います。
大事なことなので、しつこく何度か書きます。
仮数部が 53 bit あることから、53bit 以内の整数は誤差なく表現可能です。
単精度・半精度 浮動小数点数
64bitより正確な浮動小数点数
Colab #
浮動小数の考え方を Colab で確認してみましょう。
math.frexp
という関数は、仮数が [0,1] となるように指数を調整した結果を示します。
math
モジュールを利用可能にします。
import math
math.frexp(数)
という式を、数の部分を変えながら試してみましょう。
2進数の表記で考えると、小数点を移動させていることが見やすいですね。
math.frexp(0.09375)
math.frexp(123.0)