前回の記事では、float8_e5m2が表現できる有限値の範囲は、−57344から57344までであることを確認した。
1000はこの範囲に含まれている。しかし、1000を正確に表すbit列は存在しないため、保存すると1024になった。
1000の前後で表現できる数値は、896と1024だった。
1000 - 896 = 104
1024 - 1000 = 24
1024の方が近いので、1000は1024に丸められると考えられる。
では、ちょうど真ん中だったらどうなるのだろうか?
896と1024の中間は、960だ。
(896 + 1024) / 2 = 960
川口なら、その日の気分で選びそうだが、コンピュータはどうするのだろうか?
どうでもいいが、川口今の気分は「896」。
実験してみよう。
import torch
for value in [960.0, 1000.0]:
x = torch.tensor(
value,
dtype=torch.float32,
).to(torch.float8_e5m2)
print(f"{value} → {x.float().item()}")
結果は次のようになる。
960.0 → 1024.0
1000.0 → 1024.0
960も1024になった。
896と1024までの距離は同じなのに、なぜ1024が選ばれたのだろうか?
ちょうど真ん中の場合は、どう丸める?
表現できない数値を、表現可能な数値に置き換えることを「丸め」という。
今回の変換で使われるのは、**最近接丸め(偶数)(round to nearest, ties to even)**という規則だ。(AI先生より)
参考:Wikipedia:偶数への丸め(round to even)
基本的には、最も近い値を選ぶ。
ただし、ちょうど中間だった場合は、有効数字の最後のbitが0になる方を選ぶ。
「偶数」と聞くと、896も1024も偶数じゃないかと思ってしまう。
ここで確認するのは、十進数として偶数かどうかではなく、2進数の有効数字の最後のbitだ。
前回確認したbit列を見てみよう。
| 数値 | 8 bitの表現 |
|---|---|
| 896 | 01100011 |
| 1024 | 01100100 |
今回の形式では、8 bitの最後がFractionの最後のbitに当たる。
896の最後のbitは1、1024の最後のbitは0なので、中間値の960は1024に丸められる。
では、ちょうど中間なら、いつも大きい方になるのだろうか?
他の数値でもやってみよう。
896のbit列01100011を符号なし整数として扱い、1を引くと、01100010になる。これは768を表す。
| 数値 | 8 bitの表現 |
|---|---|
| 768 | 01100010 |
| 896 | 01100011 |
768と896の中間値は、832だ。
(768 + 896) / 2 = 832
先ほどのPythonコードに832.0を追加して、やってみよう。
import torch
for value in [832.0, 960.0, 1000.0]:
x = torch.tensor(
value,
dtype=torch.float32,
).to(torch.float8_e5m2)
print(f"{value} → {x.float().item()}")
結果は次のとおり。
832.0 → 768.0
960.0 → 1024.0
1000.0 → 1024.0
832は768と896の中間値で、最後のbitが0なのは768なので、今回は小さい方の768に丸められた。
なるほど、コンピュータは気分で選んでいるわけではなかった。
今回の規則をまとめると、次のようになる。
- 中間値ではない場合:隣り合う表現可能な2つの値のうち、距離が近い方を選ぶ。
- ちょうど中間の場合:その2つの値の有効数字の最後のbitを確認し、0になる方を選ぶ。