float型では123456789すらも表現できない話

# はじめに
この記事は[QualiArts Advent Calendar 2020](https://qiita.com/advent-calendar/2020/qualiarts)の22日目の記事になります。

本稿では数値の扱われ方の説明を行ない、浮動小数点数で表現できる整数値の有効桁数の話をします。
そして、最終的に単精度浮動小数であるfloat型では 123456789 が表現できないことを確認していきます。[^1]

# 背景
ゲームの演出で獲得経験値をパラパラとアニメーションでカウントアップさせようとしていました。
とりあえず 0 から 123456789 までカウントアップさせる実装をしたものの、123456789 になってくれない。
<img width="360" alt="カウントアップ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/94831ecc-d45e-82f1-24fc-db3b7ad82568.gif"></img>
くそぅ、、何度やっても **<font>1234</font>567<font color="red">92</font>** になってしまう！！！

結論から言うと、カウントアップの内部処理で値がfloat型として扱われており、float型では 123456789 の値は表現できなかったというオチでした。

int型の最大値は 2,147,483,647 で、float型の最大値は $3.402823466 × 10^{38}$ 。
何となくfloat型は大きな値も表現できると思っていたのですが、これは<font color="red">**表現できる最大の数を表しているだけで、最大値までの全ての値を表現できると言っているわけではなかった**</font>のですね。
冷静に考えればその通り。お恥ずかしい話です。

せっかくの機会なので数値の表現周りを一から勉強をし直しました。
STK（その情報、ためになる人、きっといる）精神で本稿にまとめたいと思います。

値がズレた原因の説明のために、本稿では数値の扱われ方周りを細かく見ていきます。

# 固定小数点数

まずは固定小数点数という表現を見ていきます。
固定小数点数とは「小数点を固定して表現された数」です。

固定小数点数は**「<font color="red">〇〇</font><font color="blue">.</font><font color="green">□□</font>」**のように書くことができます。
<font color="red">**〇〇**</font>が<font color="red">**整数部**</font>、<font color="green">**□□**</font>が<font color="green">**小数部**</font>、<font color="blue">**.**</font>は<font color="blue">**小数点**</font>です。
そして<font color="orange">**△△進数**</font>で固定小数点数を書くときには、<font color="red">**整数部**</font>と<font color="green">**小数部**</font>はそれぞれ<font color="orange">**△△進数**</font>で表現されます。

と、文字を羅列して説明しても分かりづらいので図示すると次のようになります。
<img width="300" alt="固定小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/63b089a0-a216-a6ad-0a58-244be5b967e3.png"></img>

## 10進数の固定小数点数

それでは<font color="orange">**10進数**</font>の固定小数点数を見ていきます。
先ほどの図を<font color="orange">**10進数**</font>に変更すると次のようになります。
<img width="300" alt="10進数の固定小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/04d83f76-bb32-5455-a6c9-997e70cb1599.png"></img>
<font color="red">**整数部**</font>と<font color="green">**小数部**</font>がそれぞれ<font color="orange">**10進数**</font>で表現されていることが分かります。
これは僕らが日常でよく行なっている表現方法ですね。

### 10進数での固定小数点数の例

日本語的におかしいですが、21.75 を<font color="orange">**10進数**</font>の固定小数点数で表すと <font color="red">**21**</font><font color="blue">**.**</font><font color="green">**75**</font> となります。

## 2進数の固定小数点数

次は<font color="orange">**2進数**</font>の固定小数点数を見ていきます。
<font color="orange">**2進数**</font>の固定小数点数を図で表すと次のようになります。
<img width="300" alt="2進数の固定小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/9067356a-e86c-071f-f956-5d0a67236ed9.png"></img>
<font color="red">**整数部**</font>と<font color="green">**小数部**</font>がそれぞれ<font color="orange">**2進数**</font>で表現されていることが分かります。

### 2進数での固定小数点数の例

それでは、21.75 を2進数の固定小数点数で表します。
またこの時、全体を 32 ビット（ 4 バイト）で表現するとします。

- <font color="red">**0001**</font><font color="red">**0101**</font><font color="blue">**.**</font><font color="green">**110000000000000000000000**</font>
- <font color="red">**0000**</font><font color="red">**0001**</font><font color="red">**0101**</font><font color="blue">**.**</font><font color="green">**11000000000000000000**</font>
- <font color="red">**0000**</font><font color="red">**0000**</font><font color="red">**0001**</font><font color="red">**0101**</font><font color="blue">**.**</font><font color="green">**1100000000000000**</font>
- <font color="red">**00000000000000010101**</font><font color="blue">**.**</font><font color="green">**1100**</font><font color="green">**0000**</font><font color="green">**0000**</font>

すると、同じ 21.75 でも**複数の表現方法がある**ことが分かります（2進数の計算方法は後ほど説明します）。

### 2進数の固定小数点数のビット構成

今回は 16 ビット/ 16 ビットの分け方で見てみましょう。

- <font color="red">**0000**</font><font color="red">**0000**</font><font color="red">**0001**</font><font color="red">**0101**</font><font color="blue">**.**</font><font color="green">**1100000000000000**</font>

この時の固定小数点数のビット構成は次の図のようになります。
<img width="600" alt="固定小数点数のビット構成" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/1e565569-e429-9412-9080-56932ce150ad.png"></img>
ここに先ほどの数値が入ると次のようになります。
<img width="600" alt="固定小数点数のビット構成" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/fb9c7088-5e1f-a5f0-ed7d-558754ee85e0.png"></img>

### 2進数の固定小数点数の整数部

それでは<font color="red">**整数部**</font>を見ていきましょう。
<img width="600" alt="2進数の固定小数点数の整数部" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/dab674ff-e8b2-7801-0580-b374c4d10e30.png"></img>
2進数の整数部は一番右が10進数の $2^0$ を表し、左に行くにつれて $2^1$、$2^2$ ...という大きな数字を表します。

そのため今回の2進数を10進数に直すと、次の計算式により 21 になります。
$2^0+2^2+2^4=1+4+16=21$

また、この部分の説明では整数部は正の数しか扱わないと仮定しています。
負の数の説明は後ほど行います。

### 2進数の固定小数点数の小数部

次は<font color="green">**小数部**</font>を見ていきましょう。
<img width="600" alt="2進数の固定小数点数の小数部" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/9874f395-0e79-77bf-dd82-e2483958ae3d.png"></img>
2進数の小数部は一番左が10進数の $2^{-1}$ を表し、右に行くにつれて $2^{-2}$、$2^{-3}$ ...という小さな数字を表します。

そのため、今回の2進数を10進数に直すと、次の計算式により 0.75 になります。
$2^{-1}+2^{-2}= \frac{1}{2}+\frac{1}{4}=0.5+0.25=0.75$

これにより、先ほどの整数部の計算と合わせて、

- <font color="red">**0000**</font><font color="red">**0000**</font><font color="red">**0001**</font><font color="red">**0101**</font><font color="blue">**.**</font><font color="green">**1100000000000000**</font>

が 21.75 を表していたことが分かります。

### ビット数の違いによる影響

さて、さきほど 21.75 は**複数の表現方法がある**という話をしました。

- <font color="red">**0001**</font><font color="red">**0101**</font><font color="blue">**.**</font><font color="green">**110000000000000000000000**</font>
- <font color="red">**0000**</font><font color="red">**0001**</font><font color="red">**0101**</font><font color="blue">**.**</font><font color="green">**11000000000000000000**</font>
- <font color="red">**0000**</font><font color="red">**0000**</font><font color="red">**0001**</font><font color="red">**0101**</font><font color="blue">**.**</font><font color="green">**1100000000000000**</font>
- <font color="red">**00000000000000010101**</font><font color="blue">**.**</font><font color="green">**1100**</font><font color="green">**0000**</font><font color="green">**0000**</font>

次は小数点の位置が違うことで、どういう影響があるのかを見ていきましょう。

#### 整数部のビットが多い時

整数部のビットが多い時を見ていく例として、28 ビット/ 4 ビットの分け方を見てみます。
<img width="600" alt="整数部のビットが多い時" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/577ae8c4-ee01-7ac5-8358-b1bf4f4264b5.png"></img>
この時、整数部の最上位ビットは $2^{27}$ のため、整数部では 0 〜 268,435,455（ $2^{28}-1$ ）までの数字を扱うことができます。
つまり、<font color="red">**整数部のビットが多いと、より大きい数字を扱える**</font>ことが分かります。

また、小数部の最下位ビットは $2^{-4}$ のため、小数部では 0.0625（ $2^{-4}$ ）までの精度しか扱うことができません。
つまり、<font color="green">**小数部のビットが少ないと、細かな精度を扱えない**</font>ことが分かります。

#### 小数部のビットが多い時

小数部のビットが多い時を見ていく例として、4 ビット/ 28 ビットの分け方を見てみます。
<img width="600" alt="小数部のビットが多い時" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/92685f97-b4e2-8975-2fc2-98ee336fa64d.png"></img>

この時、整数部の最上位ビットは $2^{3}$ のため、整数部では 0 〜 15（ $2^{4}-1$ ）までの数字しか扱うことができません。
つまり、<font color="red">**整数部のビットが少ないと、小さな数字しか扱えない**</font>ことが分かります。

また、小数部の最下位ビットは $2^{-28}$ のため、小数部では 0.0000000037252903（ $2^{-28}$ ）までの精度を扱うことができます。
つまり、<font color="green">**小数部のビットが多いと、より細かな精度を扱える**</font>ことが分かります。

### ビット数の違いによる影響まとめ

ビット数の違いによる影響をまとめると次の表のようになります。

| ビット数の割り振り | 整数部 | 小数部 |
|:-----------|:------------|:------------|
| 整数部が多くて小数部が少ない | より大きな数字を扱える | 細かな精度を扱えない  |
| 整数部が少なくて小数部が多い | 小さな数字しか扱えない | より細かな精度を扱える |

### 実際に使われるビット数

では実際にはビット数はどのような割り振りで使われているのでしょうか。

実は、<font color="red">**整数部**</font>と<font color="green">**小数部**</font>が混在した固定小数点数では計算を行うのは難しいという課題があります。
そのため実際は<font color="red">**「整数部のみ」**</font>or<font color="green">**「小数部のみ」**</font>の固定小数点数が利用されることが多いです。

<font color="red">**「整数部のみ」**</font>の固定小数点数は主に整数型を扱うために利用されます。
<img width="600" alt="整数部のみ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/3f52778d-c80c-34c5-3d9b-aa249a7afc50.png"></img>
<font color="green">**「小数部のみ」**</font>の固定小数点数は主に後ほど説明する浮動小数点数内で利用されます。
<img width="600" alt="小数部のみ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/1d65eef9-cbf4-9693-a657-6af13d4a68b4.png"></img>

そして、<font color="red">**整数**</font>と<font color="green">**小数**</font>が混在した値は、後ほど説明する浮動小数点数で扱います。

## 符号付き整数

ここまでは、<font color="red">**整数**</font>を扱う際は全てのビットを整数の絶対値情報として扱い、符号なし整数（正の整数）だけを扱ってきました。
<img width="600" alt="符号なし整数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/03fe83e2-2711-3a01-0e11-4291705533ab.png"></img>
たとえばこの時は、0 〜 65,535（ $2^{16}-1$ ）の値を扱うことができました。

ここで最上位のビットを<font color="blue">**符号**</font>として扱うことで、符号付き整数を扱えるようになります。
<img width="600" alt="符号付き整数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/3fc6d95a-e770-fae8-d8c7-530697b6e5f9.png"></img>
この時、<font color="blue">**符号ビットが 0 の時は正の整数、1 の時は負の整数を表します。**</font>

そしてこの符号付き整数は大きく3つの表現方法が存在します。

|  | +11 | -11 | 正と負の変換方法 |
|:-----------|:------------|:------------|:------------|
| **符号+絶対値** | <font color="blue">**0**</font> 000<font color="red">**1**</font>0<font color="red">**1**</font><font color="red">**1**</font> | <font color="blue">**1**</font> 000<font color="red">**1**</font>0<font color="red">**1**</font><font color="red">**1**</font>  | 符号ビットを反転 |
| **1の補数** | <font color="blue">**0**</font> 000<font color="red">**1**</font>0<font color="red">**1**</font><font color="red">**1**</font> | <font color="blue">**1**</font> <font color="red">**1**</font><font color="red">**1**</font><font color="red">**1**</font>0<font color="red">**1**</font>00 | 符号ビットを反転し、絶対値ビットも全て反転 |
| **2の補数** | <font color="blue">**0**</font> 000<font color="red">**1**</font>0<font color="red">**1**</font><font color="red">**1**</font> | <font color="blue">**1**</font> <font color="red">**1**</font><font color="red">**1**</font><font color="red">**1**</font>0<font color="red">**1**</font>0<font color="red">**1**</font> | 符号ビットを反転し、絶対値ビットも全て反転し、1 を足す |
※説明を簡単化するため、8 ビット整数で説明をしています。

ここからは正負の加算処理を見ながらこの 3 つの表現方法を比較していきます。

### 符号+絶対値

|  | +11 | -11 | 正と負の変換方法 |
|:-----------|:------------|:------------|:------------|
| **符号+絶対値** | <font color="blue">**0**</font> 000<font color="red">**1**</font>0<font color="red">**1**</font><font color="red">**1**</font> | <font color="blue">**1**</font> 000<font color="red">**1**</font>0<font color="red">**1**</font><font color="red">**1**</font>  | 符号ビットを反転 |

符号+絶対値の加算処理を考えます。

たとえば +8 と -11 の 2 数を加える際には下記の手順が必要になります。

1. 各々の符号を調べる（符号が同じならば 2 つの数の絶対値を足して終了）
2. 符号が異なる場合は 2 数の数の絶対値の大きさを比べる
3. 絶対値が大きい方の絶対値から小さい方の絶対値を引く
4. 絶対値が大きい方の符号を結果の符号として採用する

式で表すと次のようになります。

( +8 ) + ( -11 ) = -( 11 - 8 ) = -3
( 0 0001000 ) + ( 1 0001011 ) = 1 ( <font>0001</font>011 - <font>001</font>000 ) = 1 0000011

このように符号+絶対値の加算では、数の比較、加算、減算、場合分けなど様々な処理が必要となるため実現するには少し手間が掛かります。

### 1 の補数

|  | +11 | -11 | 正と負の変換方法 |
|:-----------|:------------|:------------|:------------|
| **1 の補数** | <font color="blue">**0**</font> 000<font color="red">**1**</font>0<font color="red">**1**</font><font color="red">**1**</font> | <font color="blue">**1**</font> <font color="red">**1**</font><font color="red">**1**</font><font color="red">**1**</font>0<font color="red">**1**</font>00 | 符号ビットを反転し、絶対値ビットも全て反転 |

1 の補数の加算では、符号を含め 2 数を加算し、符号ビットから桁上がりがあれば<font color="red">**上がった桁を捨てて、結果に 1 を加えます。**</font>

それでは、$(+4)+(+11)$ の計算を見てみます。
<img width="200" alt="1の補数の正の値同士の加算" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/68c7e552-e066-c1f9-0d7b-ae787621a4e6.png"></img>
4（ 0 0000100 ）と 11（ 0 0001011 ）の加算の結果が確かに 15（ 0 0001111 ）になっていることを確認できます。

次に、$(-4)+(+11)$ の計算を見てみます。
<img width="200" alt="1の補数の正負の値の加算①" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/e981c939-da3b-cd26-661d-8dad28fb7e1d.png"></img>
符号ビットから桁上がりが発生していることが分かります。
なので、<font color="red">**上がった桁を捨てて、結果に 1 を加えます。**</font>
<img width="200" alt="1の補数の正負の値の加算②" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/0222c63f-33e6-8705-62e0-cfbb7dabb61e.png"></img>
これにより、-4（ 1 <font>1111</font>011 ）と 11（ 0 0001011 ）の加算の結果が確かに 7（ 0 0000111 ）になっていることを確認できます。

また 1 の補数では、+0（ 0 0000000 ）と -0（ 1 <font>1111</font>111 ）の 2 つの 0 が存在します。
例えば +11（ 0 0001011 ）と -11（ 1 <font>1110</font>100 ）を加算すると -0（ 1 <font>1111</font>111 ）になることを確認できます。
<img width="200" alt="1の補数の-0になる加算" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/71aa6455-ba06-eb72-369b-1bfeb8a62dbe.png"></img>
そして、8 ビットの時の値は、-127 〜 127 の値を扱うことができます。

### 2 の補数

|  | +11 | -11 | 正と負の変換方法 |
|:-----------|:------------|:------------|:------------|
| **2 の補数** | <font color="blue">**0**</font> 000<font color="red">**1**</font>0<font color="red">**1**</font><font color="red">**1**</font> | <font color="blue">**1**</font> <font color="red">**1**</font><font color="red">**1**</font><font color="red">**1**</font>0<font color="red">**1**</font>0<font color="red">**1**</font> | 符号ビットを反転し、絶対値ビットも全て反転し、1 を足す |

2 の補数の加算では、符号を含め 2 数を加算し、符号ビットから桁上がりがあれば<font color="red">**上がった桁を捨てます。**</font>

それでは 1 の補数の時と同様の加算を見ていきます。
$(+4)+(+11)$ の計算に関しては 1 の補数の時と同様なので省略します。

それでは、$(-4)+(+11)$ の計算を見てみます。
-4 の 2 の補数表現は 4（ 0 0000100 ）の符号ビットと絶対値ビットを反転させて 1 を足した 1 <font>1111</font>100 です。
<img width="200" alt="2の補数の正負の値の加算" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/12d59949-4e98-9080-d1e5-a50d549911df.png"></img>
この時、符号ビットから桁上がりが発生しているので<font color="red">**上がった桁を捨てています。**</font>
これにより、-4（ 1 <font>1111</font>100 ）と 11（ 0 0001011 ）の加算の結果が確かに 7（ 0 0000111 ）になっていることを確認できます。

また 2 の補数では、-0 が存在せず、+0（ 0 0000000 ）のみが存在します。
例えば1の補数の説明の際に行なった +11（ 0 0001011 ）と -11（ 1 <font>1110</font>101 ）の加算結果も +0（ 0 0000000 ）になることを確認できます。
<img width="200" alt="2の補数の0になる加算" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/9b3f4465-f055-ab42-f2b3-7b521614e356.png"></img>
そして、<font color="red">**-0 が無くなったので負の数を 1 つ多く扱えるようになるため**</font>、8 ビットの時の値は、<font color="red">**-128**</font> 〜 127 の値を扱うことができます。

### 符号付き整数表現の比較

以上を踏まえて符号付き整数表現をまとめると次の表のようになります。

|  | +0 | -0 | 最大値 | 最小値 |
|:-----------|:------------:|:------------:|:------------:|:------------:|
|**符号+絶対値**|0 0000000|1 0000000|127<br>0 <font>1111</font>111|-127<br>1 1111111|
|**1 の補数**|0 0000000|1 <font>1111</font>111|127<br>0 <font>1111</font>111|-127<br>1 0000000|
|**2 の補数**|0 0000000|<font color="red">**なし**</font>|127<br>0 <font>1111</font>111|<font color="red">**-128**</font><br>1 0000000|

この 3 つの中では、0 が 1 種類しか存在せず、表現できる範囲も広く、計算も単純な 2 の補数が最も優れていると考えられます。
そのため、現実では符号付き整数は 2 の補数で表現されることが多いです。

## 整数型との対応

以上で固定小数点数に関する説明が終わりです。
最後に整数型と固定小数点数の表現方法の対応を表にまとめると次のようになります。
※ 環境によっては異なることもあります。

| 型 | 説明 | 範囲 | 固定小数点数での表現方法 |
|:-----------|:------------|:------------|:------------|
| sbyte | 符号付き 8 ビット整数 | -128 〜 127 | 2 の補数の符号付き整数（ 8 ビット）|
| byte | 符号なし 8 ビット整数 | 0 〜 255 | 符号なし整数（ 8 ビット）|
| short | 符号付き 16 ビット整数 | -32,768 〜 32,767 | 2の 補数の符号付き整数（ 16 ビット）|
| ushort | 符号なし 16 ビット整数 | 0 〜 65,535 | 符号なし整数（ 16 ビット）|
| int | 符号付き 32 ビット整数 | -2,147,483,648 〜 2,147,483,647 | 2 の補数の符号付き整数（ 32 ビット）|
| uint | 符号なし 32 ビット整数 | 0 〜 4,294,967,295 | 符号なし整数（ 32 ビット）|
| long | 符号付き 64 ビット整数 | -9,223,372,036,854,775,808<br>〜 9,223,372,036,854,775,807 | 2 の補数の符号付き整数（ 64 ビット）|
| ulong | 符号なし 64 ビット整数 | 0 〜 18,446,744,073,709,551,615 | 符号なし整数（ 64 ビット）|

# 浮動小数点数

次は浮動小数点数という表現を見ていきます。
浮動小数点数とは「小数点を浮動して（一定の場所に定めずに）表現された数」です。

固定小数点数の時と同様の説明を行うと、浮動小数点数は**「<font color="blue">±</font><font color="green">〇〇</font>×<font color="orange">△△</font>の<font color="red">□□</font>乗」**のように書くことができます。
<font color="blue">**±**</font>が<font color="blue">**符号**</font>、<font color="green">**〇〇**</font>が<font color="green">**仮数（かすう）**</font>、<font color="orange">**△△**</font>が<font color="orange">**基数（きすう）**</font>、<font color="red">**□□**</font>が<font color="red">**指数（しすう）**</font>です。
そして<font color="orange">**△△進数**</font>で浮動小数点数を書くときには、<font color="green">**仮数**</font>と<font color="red">**指数**</font>はそれぞれ<font color="orange">**△△進数**</font>で表現され、<font color="orange">**基数**</font>の値は<font color="orange">**進数の値の△△**</font>と一致します。

今回も、文字を羅列して説明しても分かりづらいので図示すると次のようになります。
<img width="500" alt="浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/3b8460e0-f8df-393a-f1b9-e56ab336a112.png"></img>

## 10 進数の浮動小数点数

それでは<font color="orange">**10進数**</font>の浮動小数点数を見ていきます。
先ほどの図を<font color="orange">**10進数**</font>に変更すると次のようになります。
<img width="500" alt="10進数の浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/749e9fed-7e10-f6a1-930c-967b78430315.png"></img>
<font color="green">**仮数**</font>と<font color="red">**指数**</font>がそれぞれ<font color="orange">**10進数**</font>で表現され、<font color="orange">**基数**</font>の値が <font color="orange">**10**</font> になっていることを確認できます。

### 10進数での浮動小数点数の例

それでは、21.75 を<font color="orange">**10進数**</font>の浮動小数点数で表します。
<img width="300" alt="様々な10進数の浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/0d16813a-7c7a-d33a-f1a8-7ff5801f2208.png"></img>
すると、上記のように同じ 21.75 でも**複数の表現方法がある**ことが分かります。
これを**一意に決めることができないか**を考えます。

### 浮動小数点数の正規化表現

<font color="orange">**基数の値をR**</font>、<font color="green">**仮数の値をm**</font>とした時に、0 以外の値は 1 ≦ <font color="green">**m**</font> < <font color="orange">**R**</font> となるように表現（ **正規化表現** ）できます。
つまり、先ほどの 21.75 を浮動小数点数の正規化表現で表すと、
<img width="300" alt="正規化後の10進数の浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/add36ba4-4d0f-37e3-c804-7d3f1b7f1234.png"></img>
のようになり、**浮動小数点数を一意に表すことができる**ようになります。

## 2進数の浮動小数点数

次は<font color="orange">**2進数**</font>の浮動小数点数を見ていきます。
<font color="orange">**2進数**</font>の浮動小数点数を図で表すと次のようになります。
<img width="500" alt="2進数の浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/acda487a-de42-7741-df63-2c629c9f0522.png"></img>
<font color="green">**仮数**</font>と<font color="red">**指数**</font>がそれぞれ<font color="orange">**2進数**</font>で表現され、<font color="orange">**基数**</font>の値が <font color="orange">**2**</font> になっていることを確認できます。

### 2進数での浮動小数点数の例

それでは、 21.75 を<font color="orange">**2進数**</font>の浮動小数点数で表します。
<img width="400" alt="様々な2進数の浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/54d5b01e-2d14-aae7-7534-aaeec13b2f13.png"></img>
すると、<font color="orange">**10進数**</font>の浮動小数点数の時と同様に、同じ 21.75 でも**複数の表現方法がある**ことが分かります。

### 浮動小数点数の正規化表現

21.75 も先ほどと同様に正規化表現を用いて**一意に表す**ことができます。
<img width="400" alt="正規化後の2進数の浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/1e0c37f2-e96a-7b8f-14e1-28b6e7c46944.png"></img>
<font color="orange">**2進数**</font>の浮動小数点数ではこの値を元にビット構成を構築します。

### 2進数の浮動小数点数のビット構成（単精度浮動小数点数）

今回は 32 ビットの浮動小数点数（単精度浮動小数点数）のビット構成を見ていきます。
この時の浮動小数点数のビット構成は次の図のようになります。
※ 今回は浮動小数点算術に関する標準であるIEEE754におけるビット構成で説明を行います。
<img width="600" alt="単精度浮動小数点数のビット構成" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/9f3319d8-5512-9095-1fd1-480021e9315d.png"></img>
ここのビット構成に先ほど正規化した値を当てはめていきます。
<img width="400" alt="正規化後の2進数の浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/1e0c37f2-e96a-7b8f-14e1-28b6e7c46944.png"></img>

### 浮動小数点数の符号部

浮動小数点数では<font color="blue">**符号ビット**</font>が 0 の時は正の実数、1 の時は負の実数を表します。
また、浮動小数点数では負の時に他のビットを反転させたりはしません。
固定小数点数の符号+絶対値がイメージに近いかもしれません。
<img width="70" alt="浮動小数点数の符号部" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/dd0e0cd2-f1ee-49b3-d836-fbd8325799d5.png"></img>
今回は正の値なので<font color="blue">**符号ビット**</font>には 0 が入ります。

### 浮動小数点数の指数部

浮動小数点数では指数は負の値も表現できるようにします。
この時、<font color="red">**指数部**</font>は符号付き整数になるのですが、<font color="red">**指数部**</font>には**実際の指数に +127 をした値を代入**することで符号付き整数を表現します。
これは符号+絶対値、1 の補数、2 の補数のどれとも違う表現になります。

<font color="red">**指数部**</font>の値と実際の指数（ 2 のべき乗数）の関係をまとめると次の表のようになります。
<img width="800" alt="浮動小数点数の指数と指数部の関係" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/d72affc1-29f5-9ce7-f249-199ff9809ac8.png"></img>
また、<font color="red">**指数部**</font>が 0 や 255 の時は、0 や無限大などを表したりと特殊な使われ方をします。

今回は $2^4$（ $2^{0000100}$ ）を表したいため、4 に 127 を加算した 131（ <font>0000</font>0100 に <font>0111</font>1111 を加算した 10000011 ）を<font color="red">**指数部**</font>に入れます。
<img width="300" alt="浮動小数点数の指数部" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/493cca57-622f-f894-9c3d-d9aa51ea88d3.png"></img>

### 浮動小数点数の仮数部

正規化をしたことにより、<font color="orange">**2進数**</font>の<font color="green">**仮数部**</font>の最上位ビットは必ず 1 になります。
<img width="400" alt="正規化後の2進数の浮動小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/0e111d4f-b7f7-c234-8eb7-c1a15b94c4c1.png"></img>
そのため、より多くの数値を表現するために、<font color="green">**仮数部**</font>には**最上位ビットを省略して値を格納**します。
<img width="600" alt="浮動小数点数の仮数部" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/7f86f2b1-9bfc-9a7c-b791-e02fb94ef935.png"></img>
ちなみにここで先ほど説明した小数のみの固定小数点数が用いられています。

### 21.75 の浮動小数点数のビット構成

以上を合わせることで、21.75 の浮動小数点数のビット構成は次のようになります。
<img width="600" alt="10進数の固定小数点数" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/2d61c895-4400-aedd-013c-4ee71bd6cc74.png"></img>

## 実数型との対応

ここまでは単精度浮動小数を見ていきましたが、浮動小数は大きく半精度浮動小数、単精度浮動小数、倍精度浮動小数などがあります。
それぞれのビット構成は次のようになります。
<img width="800" alt="各精度の浮動小数点数のビット構成" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/765db9aa-102d-bd83-95c1-b5182906cbbb.png"></img>
以上で浮動小数点数に関する説明は終わりです。
最後に実数型と浮動小数点数の表現方法の対応を表にまとめると次のようになります。
※ 環境によっては異なることもあります。

| 型 | 種類 | 範囲 |
|:-----------|:------------|:------------|
| half | 半精度浮動小数（ 16 ビット） | 最小の正の数  $6.103515 × 10^{-5}$<br>最大の正の数  $6.5504 × 10^{4}$ |
| float | 単精度浮動小数（ 32 ビット） | 最小の正の数  $1.175494351 × 10^{-38}$<br>最大の正の数  $3.402823466 × 10^{38}$ |
| double | 倍精度浮動小数（ 64 ビット）| 最小の正の数  $2.2250738585072014 × 10^{-308}$<br>最大の正の数  $1.7976931348623158 × 10^{308}$ |

# float型で 123456789 が表現できなかった原因

先ほどのfloat型の表現範囲を改めて確認します。

| 型 | 種類 | 範囲 |
|:-----------|:------------|:------------|
| float | 単精度浮動小数（32ビット） | 最小の正の数  $1.175494351 × 10^{-38}$<br><font color="red">**最大の正の数  $3.402823466 × 10^{38}$**</font> |

この表を見ると、123456789 は最大の正の数に収まっている気がします。
しかし、この最大の正の数は<font color="red">**表現できる最大の値を表しているだけであり、最大値までの全ての値を表現できるとは言っているわけではありません。**</font>

それではfloat型の整数の有効桁数を見ていきます。

## float型の整数の有効桁数

単精度浮動小数の<font color="blue">**符号部ビットが 0**</font> 、<font color="green">**仮数部ビットが全て 0**</font> で、<font color="green">**仮数部の最下位ビットが $2^1$**</font> を表す時が、float型で値を飛ばさずに表現できる整数の最大値です。
<img width="800" alt="float型の有効桁数①" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/51e7edfc-e734-03d6-f828-02617f2cb68d.png"></img>
ちょっとイメージが付きづらいなと言う方は、単精度浮動小数の<font color="blue">**符号部ビットが 0**</font> 、<font color="green">**仮数部ビットが全て 1**</font> で、<font color="green">**仮数部の最下位ビットが $2^0$**</font> を表す時が、float型で値を飛ばさずに表現できる整数の最大値<font color="red">**の 1 つ前**</font>と言った方がイメージが付きやすいかもしれないです。
<img width="800" alt="float型の有効桁数②" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/9b9cdbc1-d738-1874-fd42-0ab62ad67d23.png"></img>
また、浮動小数表示の<font color="red">**指数が 0（指数部が 127 ）**</font>のとき、<font color="green">**仮数部の最上位ビットは $2^{-1}$**</font> を表します。
<img width="800" alt="float型の有効桁数③" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/ba9ac728-00eb-3638-8e0b-b96a39e689c7.png"></img>
今回は、<font color="green">**仮数部の最下位ビットが $2^1$**</font> を表してほしいため、<font color="green">**仮数部の最上位ビットは $2^{23}$**</font> を表す必要があります。
そのため、<font color="red">**指数が 24（指数部が 151 ）**</font>になる必要があります。
<img width="800" alt="float型の有効桁数④" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/481feb06-296c-e048-8bdb-3be9c2383653.png"></img>
それを元に<font color="red">**指数部**</font>に値を代入すると次のようになります。
<img width="800" alt="float型の有効桁数⑤" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69978/6f18ef57-1d7d-010e-cb23-8a3a8e45f6ce.png"></img>
この時の値は、16777216（ $2^{24}$ ）です。
これ以降は、表現できる値が飛び飛びになってしまいます。

16777215 → 16777216 → <font color="#CCCCCC">(16777217)</font> → 16777218 → <font color="#CCCCCC">(16777219)</font> → 16777220
※<font color="#CCCCCC"> ( ) </font>の値は最下位ビットの表す数が $2^1(=2)$ のため表現できない。

よって、float型で値を飛ばさずに表現が行える整数xの範囲は、
-16777216 ≦ x ≦ 16777216

そして、123456789 は 16777216 よりも大きく、float型によって表現できない値だったため正しく表示が行われませんでした。

以上より、<font color="red">**float型で正確に表現できる整数は 7 桁まで**</font>ということがわかりました。

## double型の整数の有効桁数

ちなみに、double型で値を飛ばさずに表現が行える整数xの範囲は、$2^{53} ≒ 9.0071993 × 10^{15}$ なので、
$-9.0071993 × 10^{15} ≦ x ≦ 9.0071993 × 10^{15}$

つまり、<font color="red">**double型で正確に表現できる整数は 15 桁まで**</font>ということがわかりました。

# まとめ
本稿では数値の扱われ方の説明を行ない、浮動小数点数で表現できる整数値の有効桁数の話をしました。
その結果、float型の有効桁数は 7 桁、double型の有効桁数は 15 桁ということがわかりました。
そして、float型で 123456789 が表現できないことも確認しました。

カウントアップで 123456789 が表示されなかった問題は気付けば単純な有効桁数の問題でした。
今回はたまたま 123456789 という数字を入れたから気付いたものの、普段から有効桁数は気をつけないとなと思いました。
特に内部でfloat型に変換されていないかなどは意識して見ないといけないです。
あとは実装時は有効桁数に収まっているから問題なかったのに、運用で桁数が増えた結果、いつのまにか有効桁数に収まらなくなっているとかも気をつけないといけないですね。恐ろしい話です。

本稿がどなたかの役に立ちましたら幸いです。

[^1]: 浮動小数点算術に関する標準であるIEEE754のfloat型における話にはなります。