0.1 + 0.1 は 0.2 ではない

こんにちは。開発担当のマットです。

コンピューターはとても力強い計算機です。
毎秒毎秒、何億回も計算をできる、素晴らしい機械ですけれども、それでも 0.1 + 0.1 のような簡単な計算を正確にできないことは驚きの事実です。

2進法と10進法の違い

我々人間は、だいたい10進法を使います。
(一部の文化で、12進法や26進法などを使うこともあるようですが)

数える時は 0 で始まる。その次は 1, 2, 3, 4, 5, 6, 7, 8, 9。
合わせて、10個の記号を使います。
そのため、「十」以上を数える場合、左に位を上げます。
百と千などは「10×10」と「10x10x10」となります。

つまり、6308 の数は、
     6 x (10 x 10 x 10)
+   3 x (10 x 10)
+   0 x 10
+   8 x 1
です。

コンピュータは、2進法を使います。
数える時は 0 で始まる。その次は 1。その2記号だけです。
でも、同じ
つまり、25 の数は、 
     1 x (2 x 2 x 2 x 2) 
+   1 x (2 x 2 x 2)
+   0 x (2 x 2)
+   0 x 2
+   1 x 1
なので、2進法で書くと、25(二十五) は 「11001」 と表します。
(それは一万一千一ではなく、二十五です。別の書き方をしているだけです。)

2進法は1679年に、ドイツで開発されました。

浮動小数点」を書く時は?

233や3122 のような数字を「整数」と呼びます。
逆に、0.524 や 0.216 のような小数点のある数字を「浮動小数点数」と呼びます。

整数と同じようですが、2 を使うではなく、分数ですので、1/2 を使います。
つまり、 0.125 は
     0 x (1/2)
+   0 x (1/2 x 1/2)
+   1 x (1/2 x 1/2 x 1/2)
なので、2進法で浮動小数点数として書くと、0.125 は 001 と書きます。

ところが、2進法で、綺麗に表せない数字もあります。
例えば、10進法で、1 を 3 に割ると、0.33333333333…. になると同様、
0.1 を浮動小数点数として書く場合、
0.1 = 0.00011001100110011001100110011… になります。
(最後の「0011」の部分が永遠に繰り返します)

永遠に計算できない罠

残念ながら、コンピューターは「永遠」や「無限」の概念を理解できません。
無限に長い数字をメモリーに入れることはできませんので、省略する必要があります。

そのため、0.1 (10分の1)をメモリーに保存する場合、
0.0001100110011001101 として保存されます。

その値は、正確に 0.1 (10分の1) ではなく、0.1000003814697265625 です。
省略しているため、値が微妙にズレてしまいました。

人間として、0.1に0.1を足せば、0.2になるだろう、と思いがちです。
プログラマーも人間なので、その前提でコードを書いてしまいます。
このようなことをすると、変なエラーと予期できない動作が発生してしまいます。

まとめ

コンピューターは完全に思う通りに動くと思いたいですが、あくまでも物理的な機械なので、物理的な限界もあります。

プログラマーになるには、コーディング法や言語の特徴を覚える必要はもちろんありますが、匠と同様に、失敗をしたくない場合、道具として使っているコンピューターのことも、深く理解しなければなりません。

とても面白いと思います。


コメント