dポイントプレゼントキャンペーン実施中!

perlでlog2を計算するにはどのようにしたらよいのでしょうか。
perldocによると
sub log2 {
my $n =shift;
return log($n)/log(2);
}
でよいはずですが、log2 が「正しく整数を返すかどうか」は保証されていないので、時として問題があるようです。

通常、log2 の結果 $a を単に print $a などとして出力する分には Perl が適当? に判断して丸め処理をしてる傾向があるようですが、これを
printf "%d", $a
とすると、本当に整数部だけが出力され、演算精度によっては意図
しない数値になる場合があるとのこと、計算機環境にインストール
した Perlで、演算精度を上げるオプションを追加した場合などで、実際に出力結果が異なる、との報告を受けました。

宜しくお願い致します。

A 回答 (3件)

> このような数学的に正しい結果を求めておりますがいかがでしょうか



数学的に正しいなんていったら、コンピュータで計算させるのは
ある意味間違いです。
きちんと説明すると長くなるのでとても書ききれませんが、
通常、小数部を含む数字は一部の例外を除いてあくまで
「近似値」でしかありません。

ですので、
log[e](16)/ log[e](2)

自然対数だろうが常用対数だろうが、ある数値の
対数を取った時点で既に近似値に成り下がりますから、
ある程度の誤差が出るのはどうしようもありません。

誤差が出るのを見込んだ上で、補正するなりなんなり
する必要があります。

たとえば 2^1 ~ 2^53 までの log2 を質問にもある式で求めると
以下のようになります。

>perl -e "printf qq{%16.0f: %25.17f\n},2**$_,log(2**$_)/log(2) for
(1..53)"
2: 1.00000000000000000
4: 2.00000000000000000
8: 3.00000000000000000
16: 4.00000000000000000
32: 5.00000000000000000
64: 6.00000000000000000
128: 7.00000000000000000
256: 8.00000000000000000
512: 9.00000000000000000
1024: 10.00000000000000000
2048: 11.00000000000000000
4096: 12.00000000000000000
8192: 13.00000000000000000
16384: 14.00000000000000000
32768: 15.00000000000000000
65536: 16.00000000000000000
131072: 17.00000000000000000
262144: 18.00000000000000000
524288: 19.00000000000000000
1048576: 20.00000000000000000
2097152: 21.00000000000000000
4194304: 22.00000000000000000
8388608: 23.00000000000000000
16777216: 24.00000000000000000
33554432: 25.00000000000000000
67108864: 26.00000000000000000
134217728: 27.00000000000000000
268435456: 28.00000000000000000
536870912: 29.00000000000000400
1073741824: 30.00000000000000000
2147483648: 31.00000000000000400
4294967296: 32.00000000000000000
8589934592: 33.00000000000000000
17179869184: 34.00000000000000000
34359738368: 35.00000000000000000
68719476736: 36.00000000000000000
137438953472: 37.00000000000000000
274877906944: 38.00000000000000000
549755813888: 39.00000000000000700
1099511627776: 40.00000000000000000
2199023255552: 41.00000000000000000
4398046511104: 42.00000000000000000
8796093022208: 43.00000000000000000
17592186044416: 44.00000000000000000
35184372088832: 45.00000000000000000
70368744177664: 46.00000000000000000
140737488355328: 47.00000000000000700
281474976710656: 48.00000000000000000
562949953421312: 49.00000000000000000
1125899906842624: 50.00000000000000000
2251799813685248: 51.00000000000000700
4503599627370496: 52.00000000000000000
9007199254740992: 53.00000000000000000

ところどころ全部ゼロであるはずの小数部に
ゼロ以外の数字が登場しています。
log(2)の精度を上げるなどすればもっと小数部の
深いところで出るようにはできますが、
根本的にはやり方をまるきり変えないと解決できませんし、
2のべきの数字なら求めるのはある意味簡単ですが、
どのような入力に対しても「数学的に正しい値」というのは
無理でしょう。

有理数演算とか記号処理とかやればそれなりに正確さは求められるでしょうけど。

参考情報として、
Perlではありませんが
Binary logarithm - Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Binary_logarithm
にもうちょっと頑張ってlog2の値を求めているやり方があります。
    • good
    • 0
この回答へのお礼

チェックを忘れてしまっており、お礼が遅れまして大変失礼しました。数学的正しさは無理であることがわかりました。
2進数化し、桁数の算数を行うことで対応しました。

お礼日時:2007/11/18 03:36

$n が 2 のべきのときだけを考えるなら, ハッシュを使えばいいんでしょうねぇ.


my %log2 = map { 2**$_, $_ } 0..52;
とか (52 は適当に変えてください).
そうじゃない ($n として 2 のべき以外の値も必要) と, 2 を底とする対数の値が超越数になるので「数学的に正確」な値を小数で表現するのは不可能です.
    • good
    • 0
この回答へのお礼

チェックを忘れてしまっており、お礼が遅れまして大変失礼しました。数学的正しさは無理なので、2進数になおして、桁数でチェックするなどの方法でプログラムを作成することで対応しました。

お礼日時:2007/11/18 03:34

括弧をつけて正確、というのを強調されてますが、


log(2) とか log($n) という計算をした時点で
すでに近似値でしかないのですが、さらにその近似値を二つ使って
計算した値でどんな「正確さ」を求めているのでしょうか?

> 演算精度を上げるオプションを追加した場合などで、実際に出力結果が異なる、

そりゃあ結局近似値なんですから、精度を変えれば
値(の末尾の方)が変わるのは当たり前じゃないですか?

> log2 が「正しく整数を返すかどうか

というのが問題なら、

> log($n)/log(2);

int(log($n)/log(2));

とするとか。

で、質問者さんが本当にやりたいこと、最終的な解決を求めている
ことがらとはどんなことなのでしょうか?

この回答への補足

お世話になります。宜しくお願い致します。質問は言葉足らずでした。「正確」とは数学的正確さです。

例えば $nが16であるとした場合、
log[e](16)/ log[e](2)
=log[2](16) = 4となりますが、これを指しています。

このような数学的に正しい結果を求めておりますがいかがでしょうか。宜しくお願い致します。

補足日時:2007/10/11 16:02
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!