「2進数」⑤　固定小数点数・浮動小数点数

##コンピュータにおける数値表現
コンピュータは様々な数を扱いますが、その表現方法は2つあります。
**固定小数点数**と**浮動小数点数**です。

##固定小数点数
数を$N$ビットの範囲で表現するときに、そのうちの左から$X$ビットを整数部としてその右側に小数点を置き、残りの($N-X$)ビットを小数部として表現するような表現方法を**固定小数点数**と呼びます。

![スクリーンショット 2020-11-05 8.06.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657974/d33cd124-9322-bbc3-10d8-133c520cccbc.png)


たとえば、8ビットで2進数を表現したい時に、左から5ビットを整数部、残りの3ビットを小数部と決めておきます。
すると、この条件で「$00000000$」というビット列は「$00000.000$」となり、「$10010010$」というビット列は「$10010.010$」と表現されます。

また、8ビットで2進数を表現したい時に、左から7ビットを整数部、残りの1ビットを小数部と決めておきます。
すると、この条件で「$00000000$」というビット列は「$0000000.0$」となり、「$10010010$」というビット列は「$1001001.0$」と表現されます。

####整数を表示するときの範囲

8ビットの固定小数点数で整数を表現する場合、8ビット全てを整数部として最下位のビットの右側に小数点を置きます。
すると、この表現方法で扱うことができる数は以下の表の通りになります。

| 桁数 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |.| 10進数 |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|   |   |   |   |   |   |   |   |   |   |   |   |
|(最小値)|    | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |   | 0 |
|   |   | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |   | 1 |
|   |   | … | … | … | … | … | … | … | … |   | … |
|   |   | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |254|
|(最大値)|   | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |255|
|（最大値）+1| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  |256|

この方法では、最小値「$00000000$」から最大値「$11111111$」までの数を表現することが出来ます。
2進数の最大値を10進数に基数変換すると、$2^7＋2^6+…2^1+2^0$より、「$255$」となります。
また、最大値の1つ次の9ビットの2進数は「$100000000$」であり、この数を使って最大値を表現すると次のような数式になります。

```math
11111111=100000000-00000001
```
この式の右辺の数をそれぞれ10進数に基数変換して計算すると以下のようになります。

```math
\begin{align}
2^8-2^0&=256-1\\
&=255
\end{align}
```

したがって、全てのビットを整数として8ビットの2進数を表現した場合、最大値は「$11111111$」になります。

最大値を10進数に基数変換した値は、$2^8-1$より「$255$」となり、表現できる範囲を10進数で表すと **0〜255** までとなります。

|  | 8ビットの固定小数点数における整数 （10進数に基数変換）|
|:-:|:-:|
| 最小値 | 0 |
| 最大値  | 255 ( $2^8-1$ ) |
| 表現できる範囲| 0　〜　255|

また、全てのビットを整数としてnビットの2進数を表現した場合、最大値を10進数に基数変換すると「$2^n-1$」になり、表現できる数の範囲は「$0$〜$2^n-1$」までとなります。

|  | nビットの固定小数点数における整数 （10進数に基数変換） |
|:-:|:-:|
| 最小値 | 0 |
| 最大値  | $2^n-1$ |
| 表現できる範囲|0 〜 $2^n-1$|

####符号ビットを用いて整数を表示するときの範囲

固定小数点数で符号付きの整数を表現する場合、8ビットの2進数のうち、左から1ビットを符号ビット、そして残り7ビットを整数部として最下位のビットの右側に小数点を置きます。
すると、この表現方法で扱うことができる数は以下の表の通りになります。

| 桁数 | 符号ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 |.| 10進数 |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|   |   |   |   |   |   |   |   |   |   |   |
|(最小値)| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |   | -128 |
|   | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |   | -127 |
|   | … | … | … | … | … | … | … | … |   | … |
|   | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |   | 0 |
|   | … | … | … | … | … | … | … | … |   | … |
|   | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |126|
|(最大値)| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |127|

このとき、最小値を符号に注意して10進数に基数変換すると、$-(2^7)$より「$-128$」となります。
最大値を10進数に基数変換すると、$2^6+2^5+…2^1+2^0$より、「$127$」となります。
また、最大値「$01111111$」の1つ次の8桁の2進数は「$100000000$」（最小値と同じ数になってしまうため本来表現できませんが、便宜を図るために使用しています）であり、この数を使って最大値を表現すると次のような数式になります。

```math
01111111=10000000-00000001
```
この式の右辺の数をそれぞれ10進数に基数変換し、計算すると以下のようになります。

```math
\begin{align}
2^7-2^0&=128-1\\
&=127
\end{align}
```

したがって、左から1ビットを符号ビット、そして残り7ビット全てを整数部として8ビットの固定小数点数を表現した場合、
最小値を10進数に基数変換すると、 $-2^{(8-1)}$より「$-128$」となります。
最大値を10進数に基数変換すると、$2^{(8-1)}-1$より「$127$」となります。
この条件で表現できる数の範囲は **-128〜127** までとなります。

|  | 8ビットの固定小数点数における整数 (符号あり) |
|:-:|:-:|
| 最小値 | $-128$　( $-2^{(8-1)}$ ) |
| 最大値  | $127$　( $2^{(8-1)}-1$ ) |
|範囲| $-128$ 〜 $127$ |

次に、左から1ビットを符号ビット、そして残り全てを整数部としたnビットの2進数を表現したいと思います。
最小値を10進数に基数変換すると、「$-2^{(n-1)}$」であり、
最大値を10進数に基数変換すると、「$2^{(n-1)}-1$」となります。
この条件で表現できる数の範囲は$-2^{(n-1)}$〜$2^{(n-1)}-1$までとなります。

|  | nビットの固定小数点数における整数 (符号あり) |
|:-:|:-:|
| 最小値 | $-2^{(n-1)}$ |
| 最大値  | $2^{(n-1)}-1$ |
|範囲| $-2^{(n-1)}$ 〜 $2^{(n-1)}-1$ |

##浮動小数点数

固定小数点数は小数点の位置を固定して数を表現していましたが、これとは別に小数点を移動させることでより広範囲の数を表現する方法があります。それが**浮動小数点数**という表現方法です。

浮動小数点数は符号と**指数部**と**仮数部**から構成されています。
![スクリーンショット 2020-11-06 10.04.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657974/4a0af3ea-d4e2-1e05-c024-f52f5218d349.png)
指数部と仮数部に入る数を考える上で、**指数表記**について理解しておく必要があります。

####指数表記（10進数の場合）

たとえば、10進数「$0.00125$」を指数表記すると、以下のようになります。

| 元の数 ||指数表記| 仮数 | 基数と指数 |
|:-:|:-:|:-:|:-:|:-:|
| $0.00125$ ||$0.125\times10^{-2}$| $0.125$ | $10^{-2}$ |

仮数は、「$0.125$」のように小数点と元の数の有効な数字を含んでいます。
また、仮数は表現するときの桁数によって変化します。
上の例以外にも「$0.0125$」や、「$1.25$」として表現されることがあります。

次に、基数と指数の値について着目します。
上の例は10進数であるため、基数は「$10$」となります。
そして、指数の値についてですが、この値は仮数の表現に合わせて変化します。
上の例では元の数「$0.00125$」の仮数が「$0.125$」であり、元の数の$10^2$倍されています。
だから、仮数と指数を掛け合わせた数が元の数になるように（辻褄が合うように）、指数は「$-2$」となります。

したがって、もし仮数が「$0.0125$」である場合、基数と指数は「$10^1$」になり、
仮数が「$1.25$」である場合は基数と指数は「$10^{-3}$」になります。

| 元の数 ||指数表記| 仮数 | 基数と指数 |
|:-:|:-:|:-:|:-:|:-:|
| $0.00125$ ||$0.0125\times10^1$| $0.0125$ | $10^1$ |
| $0.00125$ ||$1.25\times10^{-3}$| $1.25$ | $10^{-3}$ |

####指数表記（2進数の場合）
2進数の指数表記の方法についても、2進数であること以外には10進数と特に大きな変わりはありません。
たとえば、「$-0.000111$」という2進数を指数表記する場合、以下のようになります。

| 元の数 ||指数表記| 仮数 | 基数と指数 |
|:-:|:-:|:-:|:-:|:-:|
| $-0.000111$ ||$-0.111\times2^{-3}$| $-0.111$ | $2^{-3}$ |

10進数の時と比べて変わっているのは基数部分です。
仮数の表現を変えてみると、他にも以下のようなパターンがあります。

| 元の数 ||指数表記| 仮数 | 基数と指数 |
|:-:|:-:|:-:|:-:|:-:|
| $-0.000111$ ||$-111\times2^{-6}$| $-111$ | $2^{-6}$ |
| $-0.000111$ ||$-000.111\times2^{-1}$| $-000.111$ | $2^{-1}$ |


####浮動小数点数の正規表現
ここまで、指数表記を使って数を表現する時に、様々なパターンがあることを確認してきました。
それでは実際に、指数表記を使って浮動小数点数を表現してみたいと思います。
指数表記を使って浮動小数点数を表現する場合には、指数表記の各部分の値を浮動小数点数の「指数部」や「仮数部」に格納する形になります。

「指数部」には指数表記の「基数と指数」のうち、指数の数だけを格納します。
そして、「仮数部」には符号を除いた指数表記の「仮数」を格納することになります。
しかし、仮数部に格納する数字には注意が必要です。
先ほどの2進数「$-0.000111$」で例えると、仮数を「$11.1$」などのように1以上の数として格納するのではなく、「$0.111$」のように、**1未満の小数の形にした上で、小数点以下の数字を仮数部に格納します**。常に小数点以下の数を格納するため、「0.」の部分は固定されていてわざわざ格納する必要がなくなります。

したがって、指数表記で仮数を「$0.111$」とした時に、仮数部に入る数は「$111$」になります。
また、指数表記で仮数を「$0.0111$」とした時に、仮数部に入る数は「$0111$」になります。

ところで、実際に浮動小数点数を表現する場合には、「指数部」や「仮数部」に格納することができるビット数が決まっていることがあります。
たとえば、先ほどの「$-0.000111$」を浮動小数点数にする場合に、もし仮数部に3ビットしか格納することが出来なかった場合について考えてみたいと思います。

| 仮数部の桁数 | 1 | 2 | 3 |（はみ出した桁）|
|:-:|:-:|:-:|:-:|:-:|:-:|
|  仮数が「$0.111$」の場合 | 1 | 1 | 1 |
|  仮数が「$0.0111$」の場合 | 0 | 1 | 1 | 1 |

この場合、両者ともに最初の「0.」の部分は含まれないことになります。
仮数が「$0.111$」の場合には続く有効な桁である「$111$」の全てを3ビットの仮数部の中に収めることが出来ます。
一方で、仮数が「$0.0111$」の場合には「0.」に続いてさらに「0111」となります。
すると、有効な桁である最後の「$1$」が3ビットの範囲からはみ出してしまいます。

このように、限られたビット数から有効な桁がはみ出してしまうと、表現したい元の数との間に誤差が生じてしまい、計算に用いられた場合に精度が低くなってしまいます。
そこで、限られたビット数に有効な桁が収まるように小数点の位置を調整します。
この調整の操作を**正規化**と呼びます。
正規化によって、円周率などの延々と続く数字の際にもできるだけ多くの有効な桁を取ることができるというメリットがあります。

したがって、正規化により仮数部では「$111$」と決まったので、指数と基数の部分は「$2^{-3}$」となります。これにより、指数部には「$-3$」という数字が入ることになります。
ここでは2進数の2の補数を用いて4ビットで表現することにします。すると、「$-3$」は「$1101$」になります。

これらをもとに、2進数「$-0.000111$」を仮数部3ビット、指数部4ビットの浮動小数点数として表現した場合は次のようになります。
符号は正なので「$0$」が入ります。そして、仮数部には「$111$」が入り、指数部には「$1101$」が入ります。

| 符号 |指④ | 指③ | 指② | 指① | 仮③ | 仮② | 仮① |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |

(※上の表では指数部1桁目を「指①」のように表現しています。)

こうして2進数「$-0.000111$」は浮動小数点数「$01101111$」となりました。

##参考にさせて頂いた書籍
きたみりゅうじ 『キタミ式イラストIT塾 基本情報技術者平成31/01年』 技術評論社 2019年

##学習してみて
今回は２進数を2通りの方法で表現することを学習したわけですが、特に浮動小数点数については非常に小さな数から膨大な数まで桁が使える限り表現できる画期的な表現方法だと感じました。
コンピュータはこんな方法を使って数を処理していたのですね。
今回は浮動小数点数のほんの一部しか扱うことが出来なかったのですが、次回は一般的に使われている形式について学習していきたいと思います。