−6.25 を単精度に変換するとき、符号・指数部・仮数を並べたビット列を16進数で表すとどれか。
- A0xC0C80000
- B0x40C80000
- C0xC0A80000
10進数を入れると、IEEE 754の半精度(16ビット)・単精度(32ビット)・倍精度(64ビット)で表したときの符号・指数部・仮数のビット列と16進数を、色分けして示します。実際に記憶される値を正確な10進数で表示し、入力との誤差も計算するので、0.1が正確に表せない理由や、形式ごとの精度と範囲の違いを確かめられます。16進数のビット列を書き換えて、値に戻すこともできます。
入力した条件はこのページのURLに入るので、ブックマークや共有をすると同じ計算結果を開けます。お気に入りと最近使ったツールは、このブラウザ内(localStorage)にのみ保存されます。
符号 指数部 仮数
丸めは最近接偶数丸めです。16進数の欄を書き換えると、そのビット列が表す値を読み取ります。
ほかの入力は今の値のまま、選んだ入力だけを今の値の1/4〜4倍の範囲で変えたときの結果です。赤い点が今の値です。
IEEE 754の浮動小数点数は、値を (−1)^符号 × 1.仮数 × 2^(指数部 − バイアス) の形で記録します。ビット数は、半精度が符号1・指数部5・仮数10、単精度が1・8・23、倍精度が1・11・52で、バイアスはそれぞれ15、127、1023です。実際の指数にバイアスを足した値を記録するのは、負の指数も符号なしの整数として扱えるようにするためです。正規化数では仮数の先頭が必ず1になるので、その1は記録しません(隠れビット)。そのため単精度は、23ビットの記録で24ビット分の精度を持ちます。ツールでは符号・指数部・仮数を色分けし、指数部を「記録された値 − バイアス = 実際の指数」の形で表示します。
0.1は2進数では0.000110011001100…と0011が無限にくり返す小数で、正規化すると1.10011001100…(2) × 2⁻⁴です。単精度では、指数部は −4 + 127 = 123(0111 1011)です。仮数は小数点以下23ビットで区切ると残りが半分より大きいので切り上げ、10011001100110011001101になります。並べると0 01111011 10011001100110011001101で、16進数では0x3DCCCCCDです。このビット列が実際に表す値は0.100000001490116119384765625で、誤差は約1.49 × 10⁻⁹です。分母が2の累乗でない分数は2進数の有限の桁で表せないため、0.1はビット数を増やしても正確には表せません。倍精度では0x3FB999999999999Aとなり、誤差は約5.55 × 10⁻¹⁸まで小さくなりますが、0にはなりません。
指数部がすべて0とすべて1のビット列は、特別な値に使われます。指数部がすべて0で仮数が0ならゼロ(符号ビットにより+0と−0)、仮数が0でなければ非正規化数です。非正規化数は先頭の1を仮定せずに 0.仮数 × 2^(1 − バイアス) を表し、最小の正規化数(単精度で2⁻¹²⁶ ≈ 1.175 × 10⁻³⁸)と0の間を等間隔に埋めます。これを漸進アンダーフローといい、最小の非正規化数は単精度で2⁻¹⁴⁹ ≈ 1.401 × 10⁻⁴⁵です。ただし値が小さくなるほど、使える仮数の桁が減って精度が落ちます。指数部がすべて1のときは、仮数が0なら無限大、0でなければNaN(非数)です。単精度で1e-40を入れると非正規化数に、1e39を入れると無限大になることをツールで確かめられます。
表せない値は最も近い表せる値に丸められ、ちょうど中間のときは仮数の最下位ビットが0(偶数)になる方が選ばれます(最近接偶数丸め)。1と、1の次に大きい表せる数との差がマシンイプシロンで、半精度は2⁻¹⁰ ≈ 9.766 × 10⁻⁴、単精度は2⁻²³ ≈ 1.192 × 10⁻⁷、倍精度は2⁻⁵² ≈ 2.220 × 10⁻¹⁶です。正規化数の範囲では、丸めによる相対誤差はこの半分以下に収まります。誤差が問題になるのは、それが計算で拡大されるときです。ほぼ等しい数の引き算では上位の桁が打ち消し合って有効な桁が減り(桁落ち)、大きさが大きく違う数の足し算では小さい方の下位の桁が失われます(情報落ち)。単精度で1.0000001を入れると1.00000011920928955078125として記憶されるので、ここから1を引くと約1.19 × 10⁻⁷となり、本来の1 × 10⁻⁷より2割近く大きくなります。100000001を単精度で入れると100000000になり、1億に1を足しても変わらないことも分かります。なお、このツールは単精度と半精度へ変換するとき、入力をいったん倍精度の値にしてから丸めるため、ちょうど中間にごく近い入力では、直接丸めた結果と最下位ビットが異なる場合があります。
半精度(FP16)は16ビットで、単精度に比べてメモリと転送量を半分にできますが、指数部が5ビットなので最大値は65504、最小の正規化数は2⁻¹⁴ ≈ 6.104 × 10⁻⁵と範囲が狭くなります。ツールでは65519は65504に丸められ、65520以上は無限大になります。AIの学習でFP16を使うときは、小さな勾配が0になるのを防ぐためにロススケーリングが使われます。bfloat16は符号1・指数部8・仮数7ビットの16ビット形式で、指数部が単精度と同じなので範囲は単精度とほぼ同じですが、精度は8ビット(10進で約2.4桁)です。単精度の上位16ビットと同じ並びなので変換しやすく、AIの学習でよく使われます。このツールはbfloat16とFP8には対応していません。bfloat16の値を知りたいときは、単精度の16進数の上位4桁をとり、下位16ビットを最近接偶数丸めで丸めます。たとえば0.1は単精度で0x3DCCCCCDで、下位のCCCDは中間の8000より大きいので切り上げ、bfloat16では0x3DCD(0.10009765625)になります。
正解:B. 2進数では無限に続く小数になるため、丸めた近い値で表すことになる
10進数の0.1は、2進数にすると0.000110011001100…と0011が無限にくり返す小数になります。浮動小数点では仮数の桁数が限られているので、途中で丸めた近い値しか記録できず、わずかな誤差が生じます。分母が2の累乗でない分数は、どれも2進数で有限の桁では表せません。一方、0.5や0.25のように分母が2の累乗の数は正確に表せます。値が10倍になるような変換は起こりません。
0x3DCCCCCDです。符号は0、指数部は123(実際の指数は −4)、仮数は1.10011001100110011001101(2)で、実際に記憶される値は0.100000001490116119384765625です。倍精度では0x3FB999999999999A、半精度では0x2E66になります。
0.1、0.2、0.3はどれも2進数で正確に表せず、それぞれ近い値に丸められるからです。倍精度では0.1と0.2はわずかに大きい値、0.3はわずかに小さい値として記憶され、ツールの誤差の符号で確かめられます。0.1 + 0.2の結果は0.3000000000000000444…となり、0.3を表すビット列(0x3FD3333333333333)とは最下位ビットが1つ違う0x3FD3333333333334になります。浮動小数点数を比べるときは、差の絶対値が十分小さいかどうかで判定します。
1と、その次に大きい表せる数との差として、単精度は2⁻²³ ≈ 1.192 × 10⁻⁷、倍精度は2⁻⁵² ≈ 2.220 × 10⁻¹⁶、半精度は2⁻¹⁰ ≈ 9.766 × 10⁻⁴です。文献によっては、この半分(丸めによる相対誤差の上限)をマシンイプシロンと呼ぶこともあるので、どちらの定義かを確かめてください。
精度が24ビットなので、2²⁴ = 16777216までのすべての整数を正確に表せます。16777217を単精度で入れると、16777216と16777218のちょうど中間なので最近接偶数丸めで16777216になり、誤差は −1 です。倍精度では2⁵³ = 9007199254740992まで、半精度では2¹¹ = 2048までです。
「ビット列(16進数)」の欄に、選んだ形式の桁数(半精度は4桁、単精度は8桁、倍精度は16桁)以内で入力すると、符号・指数部・仮数に分けて値を読み取ります。たとえば単精度では、3F800000は1、C0C80000は−6.25、7F800000は+∞、7FC00000はNaNです。先頭に0xを付けて入力してもかまいません。