見出し画像

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 で互除法を双方向で使ってみる  

いいなと思ったら応援しよう!