Perl で整数の除算ツアー (Re: 整数の除算)
皆さんよってたかってありがとうございます。お礼といっては何ですが全て試してみてみます。
まずは、一番メジャーな方法らしい。というか、やってたけど忘れてた。
int
$ perl -e 'print int(10 / 3)' 3 $ perl -Minteger -e 'print 10 / 3' 3
おっふおっふ(鳴き声) 'use integer;' なんてあるのか、ちょっと興奮。
しかし桁溢れると弱い。
$ perl -e 'print int(-6.725/0.025)' -268
「わかってる分には、簡単なの使ったらええやろ」って感じが なかだ さんっぽい。
せっかくなのでドキュメントも読んどくか。
$ perldoc -f int int EXPR int Returns the integer portion of EXPR. If EXPR is omitted, uses $_. 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 counterintu- itive 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().
ふむ、通常は int より sprintf, printf, POSIX::floor, POSIX::ceil を使えとな。
順に追ってみよう。
sprintf
$ perl -e 'print sprintf("%d", -6.725/0.025)' -268
これ、ひょっとして int と一緒じゃね? ソースまで見に行かないけど。
恥ずかしながら、floor とか ceil とか知らなかったので調べた。
- floor
- 〜以下で最大の整数:
- ceil
- 〜以上で最小の整数
らしい。単純に切り下げ・切り上げで考えてると負の時痛い目みれるそうだ。
$ perl -MPOSIX -e 'print POSIX::floor(10 / 3)' 3 $ perl -MPOSIX -e 'print POSIX::floor(-10 / 3)' -4 $ perl -MPOSIX -e 'print POSIX::ceil(10 / 3)' 4 $ perl -MPOSIX -e 'print POSIX::ceil(-10 / 3)' -3
id:kidd-number5 さんご紹介の perlfaq4 もこれと同じ「POSIX 使え」ですね。
POSIX 族は名前からして C ライブラリを呼び出してるっぽいから、double の範囲を越えると id:lyokato さんのいうように精度の問題が出そう。場合によるな。四捨五入はないみたい。別に Math::Round なんてあるみたいだけれど。
id:lyokato さんから Math::BigFloat 版。これが一番確実。
$ perl -MMath::BigFloat -e 'print Math::BigFloat->new(-6.725/0.025)->bfloor' -269
でかい数値用の floor。
$ perl -MMath::BigFloat -e 'print Math::BigFloat->new(-6.725/0.025)->bceil' -269
でかい数値用の ceil。
$ perl -MMath::BigFloat -e 'print Math::BigFloat->new(-6.725/0.025)->bfround(1)' -269
小数点第1位で四捨五入。Math::BigFloat には四捨五入もしっかりあるんですな。
id:tokuhirom さんご紹介のにぽたん研究所から正規表現版
$ perl -e ' (my $d = -6.725/0.025) =~ s/^([-+]?\d+)(?:\.\d+)?$/$1/ee; print $d; ' -269
ハッカー臭くてカコエぇー。プロダクションコードなんでやりませんが。といいつつ私には一番わかりやすかったり。バッサリスッキリ。
今、動いてるコードは正規表現版(わはぁー)ですが int に改めようと思います。引いてくる元のデータベースの該当カラムが int(1) なので 0 から 9 までしかありえないから大丈夫でしょう。求めたいものは C や Ruby で言うところの "(n + 1) / 2" です。
+-----+---+---+---+---+---+---+---+---+---+ | var | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +-----+---+---+---+---+---+---+---+---+---+ | ans | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | +-----+---+---+---+---+---+---+---+---+---+
(0 の場合は「あってたまるか、元データが悪い」という態度)
後続 Perler のためにウマく纏められると良かったんだけど、何せ色々と素養が足りないもので……スンマヘン。あー、眠くてオチも思いつきません。ごめんなさい。