Python で 0.1 を足し続けたらどうなるか?
Python で次のコードを実行したら、どうなるでしょうか? 要は、1/3 を10回足しながら値を表示しているだけなのですが。
# <プログラム1>
s=0
for i in range(10):
s=s+1/3
print(s)
「1/3」は割り切れない数ですが、実際にやってみると、5行目あたりから、それにしても何だかおかしなことになってきます。
0.3333333333333333
0.6666666666666666
1
1.3333333333333333
1.6666666666666665
1.9999999999999998
2.333333333333333
2.6666666666666665
3
3.3333333333333335
では「1/3」を「0.1」に置き換えたらどうなるでしょうか?
# <プログラム2>
s=0
for i in range(10):
s=s+0.1
print(s)
今度はきちんと表示してくるかと思いきや、やっぱりところどころでおかしなことが起こっていますね。
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
では、次のように「0.125」を足し続けたらどうなるでしょうか?
# <プログラム3>
s=0
for i in range(10):
s=s+0.125
print(s)
意外(!?)なことに、今度は期待通りの値が表示されました。
0.125
0.25
0.375
0.5
0.625
0.75
0.875
1
1.125
1.25
さて、何故なのでしょうか?
その訳は「コンピュータ内部では2進数で処理している」からです。「0.125=1/8=2⁻³」ですから、2進数で書くと「0.125₍₁₀₎=0.001₍₂₎」となります。つまり「0.125」という数は、コンピュータにしてみればキリの良い数なんですね。
それに対して10進数の「0.1」は、2進数では割り切れない数、つまりコンピュータにしてみればキリの悪い数なのです。
でも、諦めないでください。工夫のしようはあります。次のプログラムでは「0.1」を10倍して整数値に直してから足し算して、後で10で割って帳尻を合わせています。
# <プログラム4>
s=0
for i in range(10):
s=s+0.1*10
print(s/10)
こうすれば次のように期待通りの結果が得られました。
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1
わずかな誤差と思うかもしれませんが、宇宙開発のような場面を想定すると、大きな誤差になり得ます。場合によっては命取りに。ですから、コンピュータのクセを分かった上で、誤差が出ないようにするのは私たち人間の工夫のしどころです。
☆ 10進数の 1/3 , 0.1 , 0.125 を2進数の小数で表してみると、
┌ $${\dfrac{1}{3}_{(10)}=\dfrac{1}{11}_{(2)}=0.\dot{0}\dot{1}_{(2)}}$$ (割り切れない)┐
結果は ┤$${0.1_{(10)}=\dfrac{1}{1010}_{(2)}=0.0\dot{0}01\dot{1}_{(2)}}$$(割り切れない)├ となった。
└$${0.125_{(10)}=\dfrac{1}{1000}_{(2)}=0.001_{(2)}}$$ (割り切れた)┘
※ コンピュータは有限の記憶領域しか持たないので、無限小数を完全に正確に扱うことはできないと考えよう。
◇ ◇ ◇
〜 Python で整数問題を腑に落とす 〜
▷ Python で 0.1 を足し続けたらどうなるか?
▷ Python でM進数をN進数に変換する
▷ Python で互除法を双方向で使ってみる