てもとツール
ツール紹介

小数が少しずれる原因を確かめる手順|0.1の本当の値を見る

0.1と0.2を足すと0.30000000000000004になる理由を、コンピュータの中に入る本当の値で確かめる手順です。数を入れて保存される値を見る、2つの数の計算でずれが入った場所を見る、大きな整数の末尾が変わる境目を見る、ビット列から値を読む、までを画面で追います。

公開読了目安 約6分

この記事の要点

  • コンピュータの中の0.1は、0.1よりわずかに大きい値になっている
  • 数を入れると、中に入る本当の値を最後の桁まで見られる
  • 0.1 + 0.2 のずれは、読み込み2回と計算1回の3か所で入る
  • 大きな整数は、ある大きさを超えると2おきにしか入らない
  • 画面に出る短い数と、中に入っている値は別のもの
目次

プログラムで 0.1 と 0.2 を足すと、0.3 ではなく 0.30000000000000004 と出ることがあります。 浮動小数点数ビューアに数を入れると、コンピュータの中で実際にどの値になっているかが分かります。

このずれは、計算が壊れているせいではありません。 この記事では、0.1 の本当の値の見方と、足し算でずれが入る場所の確かめ方を、画面を追って説明します。

小数がずれる「あるある」3つ

小数を扱う計算をしていると、次のような場面に出会います。

  • 0.1 と 0.2 を足したら、0.30000000000000004 と出た
  • 0.1 を10回足したのに、1 ではなく 0.9999999999999999 になった
  • 17桁の番号を数として読んだら、最後の桁が変わった

どれも、コンピュータが数を持つ仕組みから起きます。

なぜ小数がずれるのか

コンピュータは、数を0と1の並び(2進数)で持ちます。 2進数でちょうど書ける小数は、0.5・0.25・0.375 のように、半分を重ねて作れる数だけです。

0.1 を2進数にすると、0.000110011001100… と同じ並びが終わらずに続きます。 入れられる桁には限りがあるので、途中で区切った「いちばん近い値」が入ります。

10進数で 1 ÷ 3 を 0.333 と書くと、少し小さくなるのと同じ形です。 区切った分だけ、0.1 とわずかに違う値が中に残ります。

浮動小数点数ビューアの使い方

ここからは、ツールの画面を順に見ていきます。

1. 数を入れて、本当の値を見る

開くと「10進の数」の欄に 0.1 が入っていて、すぐに結果が出ます。

浮動小数点数ビューアを開いた画面。10進の数の欄に0.1が入り、保存される値として0.1000000000000000055511151231257827021181583404541015625が出ている

「保存される値」の欄には、次の数が出ます。

  • 0.1000000000000000055511151231257827021181583404541015625

これが、コンピュータの中にある本当の 0.1 です。

その上の枠には「ちょうどは表せないので、最も近い値に丸めました」と出ます。 入れた数との差は +5.55112e-18 で、0.1 よりほんの少しだけ大きい値です。

「最短の表記」の欄は 0.1 のままです。 これは、中の値を表すのにいちばん短く書いた数で、本当の値そのものではありません。

2. 精度を切り替えて比べる

「精度」で「単精度(32ビット)」を選ぶと、入れられる桁が少ない形で同じ数を見られます。

精度を単精度に切り替えた画面。0.1の保存される値が0.100000001490116119384765625になり、入力との差は+1.49012e-9と出ている

0.1 は 0.100000001490116119384765625 になり、差は +1.49012e-9 に広がりました。 桁が少ない分、いちばん近い値までの距離も遠くなります。

3. 2つの数の計算で、ずれが入った場所を見る

「2つの数を計算」を選ぶと、0.1 + 0.2 が入った状態で結果が出ます。

2つの数を計算の画面。結果は0.30000000000000004で、0.3に最も近い値より1 ULP大きいと出ている。下に①から⑤の段ごとの値と差が並ぶ

結果は 0.30000000000000004 です。 その下に「書いたとおりに計算した答え 0.3 に最も近い値より、1 ULP 大きい値になりました」と出ます。

ULP は、隣り合う2つの値のあいだの1歩のことです。 つまり、結果は 0.3 と書いたときに入る値の、1つ隣の値です。

①〜⑤の段を見ると、ずれがどこで入ったかが分かります。

  • ① 0.1 を読み込むときに +5.55112e-18
  • ② 0.2 を読み込むときに +1.11022e-17
  • ④ 足した結果を丸めるときに +2.77556e-17
  • ⑤ 0.3 と書いた値は、0.3 より 1.11022e-17 小さい

0.1 と 0.2 は少し大きめに入り、0.3 は少し小さめに入ります。 そのため、足した結果と 0.3 が別の値になります。

4. 大きな整数の末尾を確かめる

「数を見る」に戻り、例のボタン「2^53+1」を押すと、9007199254740993 が入ります。

9007199254740993を入れた画面。保存される値は9007199254740992で、この大きさでは整数が2おきにしか表せないという注記が出ている

保存される値は 9007199254740992 で、最後の桁が1つ減りました。 注記には「この大きさでは、整数が2おきにしか表せません」と出ます。

16桁の数のうち、この 9007199254740992 を超えるあたりから、整数でも飛び飛びになります。 長い番号を数として扱うときは、ここで値が変わらないかを確かめておくと安心です。

5. ビット列から値を読む

「ビット列から」では、0と1や16進数で書かれた並びを、数に戻して読めます。

ビット列からの画面。9A 99 99 99 99 99 B9 3Fを貼り、バイト順を逆に読むにチェックを入れると、読んだ並びが3FB999999999999Aになり、値は0.1と出ている

ファイルの中身を書き出した並びは、前後が逆になっていることがあります。 「9A 99 99 99 99 99 B9 3F」を貼り、「バイト順を逆に読む」にチェックを入れます。 並びが 3FB999999999999A として読まれ、0.1 と出ます。

さらに詳しく使いたいとき

結果の下には、中の並びを3つの欄に分けた帯が出ます。 「符号」は正か負か、「指数」はおおよその大きさ、「仮数」は細かい数字の並びです。

欄は色と見出しの両方で分かれているので、授業で2進数に直す練習の答え合わせにも使えます。 「1つ小さい値」「1つ大きい値」の欄では、その数のすぐ隣にある値も分かります。

例の「5e-324(最小)」は、0 を除いていちばん小さい正の値です。 本当の値は1,076字もあるので、ふだんは縮めて表示し、「すべて表示」で全部の桁を出します。 「保存値をコピー」は、縮めているときも全部の桁をコピーします。

こんな場面で使える

小数のずれに出会ったときや、数の中身を確かめたいときに役立ちます。

  • 授業や自習: 2進数で小数を表す練習で、自分で直した答えと並びを見比べる
  • 計算結果の確認: プログラムで出た 0.30000000000000004 のような数が、どこから来たかを見る
  • 長い番号の確認: 17桁の会員番号や注文番号を数として扱って大丈夫かを見る
  • データの確認: 受け取ったファイルの16進数の並びが、どんな数かを読む

使用例

たとえば、情報の授業で 0.1 を2進数に直す課題が出たとします。 手で計算した 0.000110011… は、ツールの「2進の形」では 1.1001100110011… × 2^-4 と出ます。

小数点を4つ右へずらした形なので、先頭の1から後ろの並びを見比べれば、どこまで合っているかが分かります。 単精度に切り替えると、桁が少ない形での区切り方も確かめられます。

安心して使えるポイント

ツールに入力した数は、ブラウザの中だけで計算します。 外部のサーバーには送られないので、仕事のデータの値も気兼ねなく調べられます。

関連ツール

まとめ

コンピュータの中の 0.1 は、0.1 にいちばん近い別の値です。 浮動小数点数ビューアなら、その本当の値と、計算でずれが入った場所を確かめられます。

ずれを直す方法や比べ方は、0.1+0.2が0.3にならない理由と、小数の比べ方・直し方で説明しています。

補足: 開発者の方へ

ツールの倍精度は IEEE 754 の binary64 で、JavaScript の number と同じ形式です。 単精度は binary32 で、10進から直接1回だけ丸めます。

Math.fround(Number(s)) で単精度を作ると、10進 → 倍精度 → 単精度の2回の丸めになります。 単精度の2つの値のちょうど中間より少しだけ大きい数で、答えが1つずれることがあります。

最短の表記は、ECMAScript の Number::toString と同じ選び方です。 倍精度では String(x) と同じ文字列になります。

0.1 + 0.2 === 0.3; // false
(0.1 + 0.2).toString(); // "0.30000000000000004"

あわせて読みたい

同じテーマ・関連ツールの記事です。