hnwの日記

RubyとPythonとC#のround関数のバグっぽい挙動について


(12/29 20:40追記)「(追記)なぜMySQLのdecimal型を例に使ったかについて」というセクションを追加しました。また、コメントを頂戴したので返信しました。



(12/29 21:30追記)C#について言えば「Math.Round メソッド (Double, Int32)」に内部実装がどうなっているか書いてあるので仕様通りであり、誤解しようが無いという情報を頂きました。ありがとうございます。そしてごめんなさい、確かにバグじゃないです!



(12/29 21:50追記Pythonのround関数のドキュメントにも誤差が入るかもしれないという記述があります。しかし、内部実装の紹介があった方がいつどういう誤差が入るかわかるので親切かなという気がします。また、浮動小数点数の性質上誤差が入るのは仕方が無いかのような記述に見えるのですが、浮動小数点数を使っていても誤差の入らない実装がありうるのではないか、というのが今回の記事で一番お伝えしたい内容です。



(12/29 22:00追記C#のMath.Roundメソッドは四捨五入でなく偶数丸めです。どちらでも同じ結果になる数を選んでいますが、念のため。



(12/31 16:00追記)本稿の補足の意味でサンプル実装を作って別記事「ぼくのかんがえたさいきょうのround関数」にまとめました。


RubyPythonC#のround関数について、小数点以下第n位までに丸める使い方は注意が必要、もしくはそれらのround関数にバグがあるんじゃないか、という話題です。


上記の言語のround関数は、小数点以下第何位までに丸めるかを引数で指定できます。丸め対象の数は浮動小数点数ですから、1.15などをピッタリ表現できないのは仕方ありません。とはいえ、例えば1.15(に一番近い、浮動小数点数で表現できる数)を小数点以下第1位までに丸めたら1.2(に一番近い数)になってほしいところです。実際、大抵の場合はそのような挙動になります。

$ ruby -e 'x=1.15; print x.round(1), "\n";'
1.2
$ python -c 'x=1.15; print round(x, 1),"\n";'
1.2


しかし、まれに期待と異なる丸め結果になることがあります。5.015を小数点以下第2位までに丸めてみましょう。

続きを読む