• ベストアンサー

log2の「正確な」計算方法

perlでlog2を計算するにはどのようにしたらよいのでしょうか。 perldocによると sub log2 { my $n =shift; return log($n)/log(2); } でよいはずですが、log2 が「正しく整数を返すかどうか」は保証されていないので、時として問題があるようです。 通常、log2 の結果 $a を単に print $a などとして出力する分には Perl が適当? に判断して丸め処理をしてる傾向があるようですが、これを printf "%d", $a とすると、本当に整数部だけが出力され、演算精度によっては意図 しない数値になる場合があるとのこと、計算機環境にインストール した Perlで、演算精度を上げるオプションを追加した場合などで、実際に出力結果が異なる、との報告を受けました。 宜しくお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

> このような数学的に正しい結果を求めておりますがいかがでしょうか 数学的に正しいなんていったら、コンピュータで計算させるのは ある意味間違いです。 きちんと説明すると長くなるのでとても書ききれませんが、 通常、小数部を含む数字は一部の例外を除いてあくまで 「近似値」でしかありません。 ですので、 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の値を求めているやり方があります。

tk_1980024
質問者

お礼

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

その他の回答 (2)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

$n が 2 のべきのときだけを考えるなら, ハッシュを使えばいいんでしょうねぇ. my %log2 = map { 2**$_, $_ } 0..52; とか (52 は適当に変えてください). そうじゃない ($n として 2 のべき以外の値も必要) と, 2 を底とする対数の値が超越数になるので「数学的に正確」な値を小数で表現するのは不可能です.

tk_1980024
質問者

お礼

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

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.1

括弧をつけて正確、というのを強調されてますが、 log(2) とか log($n) という計算をした時点で すでに近似値でしかないのですが、さらにその近似値を二つ使って 計算した値でどんな「正確さ」を求めているのでしょうか? > 演算精度を上げるオプションを追加した場合などで、実際に出力結果が異なる、 そりゃあ結局近似値なんですから、精度を変えれば 値(の末尾の方)が変わるのは当たり前じゃないですか? > log2 が「正しく整数を返すかどうか というのが問題なら、 > log($n)/log(2); を int(log($n)/log(2)); とするとか。 で、質問者さんが本当にやりたいこと、最終的な解決を求めている ことがらとはどんなことなのでしょうか?

tk_1980024
質問者

補足

お世話になります。宜しくお願い致します。質問は言葉足らずでした。「正確」とは数学的正確さです。 例えば $nが16であるとした場合、 log[e](16)/ log[e](2) =log[2](16) = 4となりますが、これを指しています。 このような数学的に正しい結果を求めておりますがいかがでしょうか。宜しくお願い致します。

関連するQ&A