round()という、四捨五入っぽいことをする関数をいじっていて、不思議な結果に気付きました。
具体的にいうと、
round(10.50001)
11
なのに、
round(10.50000000000000000000000000001)
10
なのです。
どうやら、何桁目くらいからか、結果が変わる様子。
同じ計算をしているのに。
そこで、こんなコードを書いて、何桁目から11が10になるのか、調べようとしました。
【コード】 https://gist.github.com/kotoripiyopiyo/64598757885cae50537d870d424198d1#file-gistfile1-txt
しかし……
結果は想像の斜め上をいくものでした。こんな感じ。
下から3行目、最後が「1」じゃなくて「2」になってるのは何故!?
そして次の行で、いきなり0が全部なくなるのは何故!?
自分で調べてもよくわからなかったので、聞いて回ったところ、どうも以下のことが原因でこうなっている様子(教えてくれた友だちたち、ありがとうございます!)。
● そもそもプログラムで使う小数は小数じゃなくて「浮動小数点」ってやつで、誤差が出る小数っぽい何からしい(参考:Pythonのドキュメント、浮動小数点演算、その問題と制限、浮動小数点って何?)
● round()は四捨五入じゃなくてbankers rounding(銀行家の丸め)っていうのらしい(参考:Pythonで小数・整数を四捨五入するroundとDecimal.quantize)
これらがどう絡みあって上記のような結果になるのか、そのカラクリまでは、非常に難しく、理解に時間がかかりそうです。
ただ、問題の箇所と理由は発見できました。
僕のプログラムの場合の問題は、たとえば「10.50000001 is 11」と表示するとき、「10.50000001」の部分は、数字ではなく、こういう文字列として扱えばイケるはずです。
だから、まず10.50000001という数字の羅列を文字列として作って、そのあとで、これを数字にしてround()関数に放り込むことにしました。
【コード】
https://gist.github.com/kotoripiyopiyo/64598757885cae50537d870d424198d1#file-gistfile1-txt
すると……
はい! 望み通りの結果が出ました!
どうやらPythonのround()関数は、10.5000000000000001辺りから、答えが11→10に変わるようです。
いつもの通り全く役に立たないプログラムですが、僕がスッキリしたので、記念として取っておこうと思います。
それにしてもこの問題、根が深いのに、完全に理解しておかないと、あちこちで想定外の動きを起こしそう……
なので、とりあえず教科書はこなしながら(今は機械学習を少し離れて、『退屈なことはPythonにやらせよう ―ノンプログラマーにもできる自動化処理プログラミング』で基礎を学んでます)、パラでこの件については勉強を深めていきたいと存じます。