2015/06/24

perl で小数点切捨て

round するのに int 使わない方がいいかもしれないから sprintf とか POSIX::floor とか POSIX::ceil 使う方がいいお! って perldoc -f int に書いてたんで、実際やってみた。

int You should not use this function for rounding: one because it truncates towards 0 , and two because machine representations of floating-point numbers can sometimes produce counterintuitive results. For example, int(-6.725/0.025) produces -268 rather than the correct -269; that's because it's really more like -268.99999999999994315658 instead. Usually, the sprintf, printf, or the POSIX::floor and POSIX::ceil functions will serve you better than will int().

quoted from: perldoc int

「truncates towards 0」の方はおいておいて、小数点の例の方を試す。

$ perl -e 'print -6.725/0.025'
-269

$ perl -e 'print int(-6.725/0.025)'
-268

書かれているとおりに int 実行してみると、確かに -268(-269 を期待する場合、int はよろしくない)

sprintf してみる。

$ perl -e 'print sprintf("%d", -6.725/0.025)'
-268

む、変わんね。

$ perl -MPOSIX -e 'print POSIX::ceil(-6.725/0.025)'
-268

やっぱり変わりませんね。

$ perl -MPOSIX -e 'print POSIX::floor(-6.725/0.025)'
-269

お、キタ。

どうやら、マイナス値の小数点の丸め方が、int は単純に小数点部分切捨て(正値でも同じ)、POSIX::floor は 小さい方に寄せる(正値の時は切り捨て)という振る舞いになるっぽいという話。

int: 小数点を単純に切り捨てる。

$ perl -e 'print int(1.9999999)'
1
$ perl -e 'print int(1.0000001)'
1

$ perl -e 'print int(-1.9999999)'
-1
$ perl -e 'print int(-1.0000001)'
-1

POSIX::floor: 負数のとき小さい方に寄る

$ perl -MPOSIX -e 'print POSIX::floor(1.9999999)'
1
$ perl -MPOSIX -e 'print POSIX::floor(1.0000001)'
1

$ perl -MPOSIX -e 'print POSIX::floor(-1.9999999)'
-2
$ perl -MPOSIX -e 'print POSIX::floor(-1.0000001)'
-2

ちなみに POSIX::ceil は floor の逆を行く(正値のとき大きい方に寄り、負数で切り捨て)。

floor (ceil) は C のそれと同じ振る舞いだからまあ別にそれはそうよねという気がする。perldoc の解説が壮大すぎるのかなという印象を受けるのだけど違うっけ(演算結果の微妙な値だからか)。僕が思ってた int はそのとおりの int でした。なお iPhone の電卓アプリは floor と同じ振る舞いっぽい。となると確かに int の振る舞いを直感と違うと思う人はけっこういるか。

取り急ぎ counterintuitive という単語を覚えた!

サイト内検索