• ベストアンサー

log2の「正確な」計算方法

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

  • Perl
  • 回答数3
  • ありがとう数2

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

  • ベストアンサー
  • 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

  • FPGAによるデジタルフィルタ計算方法

    AD変換でアナログ値をデジタルに変換して、演算をして、DAでアナログ値に変換して、出力したいと考えています。 そこで、デジタル演算をFPGAで行いたいと考えていますが、以下の計算を行う場合、何か良い方法ありませんでしょうか。 係数に小数点があり、どのように演算すればいいのか、良く分かりません。 計算式は、Y=Y[n-1]+(1-a)*n   (RCフィルタ演算)とありました。 Yは、出力値、nはサンプリングデータの場所、 Y[n-1]は、一個前の演算結果の出力データ、 aが係数で、小数点0.9878等の値が入ります。 この小数点を含む計算をFPGAで実現する方法を教えてください。 あと、FPGAにこだわらず、 他に実現する良い方法ご存じでしたら、教えてください。 宜しくお願い致します。

  • c言語のプログラムについて

    今年からC言語を始めた大学1年生です。レポートで四則計算を行うプログラムを6/19までに作らないといけないです。使えるのは今までに習った選択型、反復型、関数scanfです。このプログラムの留意点は次の4つです。 1.使う演算を選択してもらう   例えば   どんな演算をしますか? 1:+,2:-,3:*,4:/ (選択) 2.演算を行う数値を入力してもらう   例えば   a + b の計算をします。aを入力してください。 3.演算結果を表示する   ○+○の答えは ○です 4.計算終了か、それともほかの演算を行うか 決める   0:計算終了 1:他の計算を行う を選択してください  以下のプログラムはこのことを留意してプログラムをつくりました。どこがおかしいのでしょうか。 #include<stdio.h> main() { short a,b,m,n; long c,d,e,f; c = a + b; d = a - b; e = a * b; f = a / b; m = 1; printf("整数を入力してください:"); scanf("%d",&a); printf("どんな演算をしますか?:"); printf("1: +,2: -,3: ×,4: ÷ :"); scanf("%d",&n); while(m=1){ if(n<=4){ while(m=1){ switch (n<=4){ case 1: printf("整数を入力してください:"); scanf("%d",&b); printf("%d + %d = %d \n",a,b,c); break; case 2: printf("整数を入力してください:"); scanf("%d",&b); printf("%d - %d = %d \n",a,b,d); break; case 3: printf("整数を入力してください:"); scanf("%d",&b); printf("%d × %d = %d \n",a,b,e); break; case 4: printf("整数を入力してください:"); scanf("%d",&b); printf("%d ÷ %d = %d \n",a,b,f); break; } } } else { printf("計算できません。"); printf("演算をやり直してください。\n:"); } } printf("計算を終了しますか、それとも他の計算を行いますか?:"); printf("0:計算終了,1:他の計算を行う:"); scanf("%d",&m); printf("計算を終了します。\n"); } 提出期限があるのでなるべく早めのご回答待ってます。生意気言ってすいません。

  • C言語初心者です。次の問題で質問です。

    (問題) 二つの整数値を読み込んで、それらの値の差が10以下でなければ、「それらの差は10以下」そうでなければ「それらの差は11以上」と表示するプログラムを作成せよ。ただし、論理OR演算子を用いること。 (自分の解答) #include <stdio.h> int main(void) { int n1,n2; printf("2つの整数を入力してください\n"); printf("整数1:"); scanf("%d",&n1); printf("整数2:"); scanf("%d",&n2); if ( (n1-n2) <= 10 || (n1-n2) >= 11) { /*ここがわかりません*/ printf("それらの差は10以下です。"); } else { printf("それらの差は11以上です。"); } return 0; } 論理OR演算子の使い方が分かりません。 解答お願いいたします。

  • logについて

    この度基本情報の試験を受けようと思っている者です。過去問を解いててどうしても分からないところがあるので、皆様のお力をお借りしたく質問いたしました。 <問題> ゼロでない整数の10進表示のけた数Dと2進表示のけた数Bとの関係を示した式はどれか。 <解答> ゼロでない整数をNとすると 10^D-1≦N≦10^D   (1) 2^B-1≦N≦2^B     (2) (1)より D-1≦log(10)N<D log(10)N<D≦log(10)N+1 したがってけた数Dが大きい場合D≒log(10)N  この解答の、D-1≦log(10)N<D から log(10)N<D≦log(10)N+1 へはどんな計算式でこうなるのかがまったく分かりません。 よろしくお願いします。 

  • log計算につきまして

    logを使った計算で困っています。 2 n-1 (←右上小文字) < 10 7(←右上小文字) < 2 n(←右上小文字) とした場合、7/log10(←右下小文字)2 < n < 7/log10(←右下小文字)2+1 となる との事ですが、どのように変換したらたどり着くのか分かりません。 どなたか教えていただけますか? よろしくお願いいたします。

  • c言語について

    C言語で、二つの整数値を読み込んで、前者の値が後者の何%であるかを実数で表示するプログラムを作成しようとして以下のコードを書きました。 #include <stdio.h> int main(void) { int n1, n2 ; puts("二つの整数を入力してください。") ; printf("整数A : \n") ; scanf("%d" , &n1) ; printf("整数B : \n") ; scanf("%d" , &n2) ; printf("Aの値はBの%f%%です。\n" , (double)(n1 / n2) * 100) ; return 0 ; } 上記コードを実行すると、0.000000%のような結果になります。 そこで、最後のキャスト演算子を使用した後の式で(n1 / n2) * 100がまずいのかなと考え、n1 / n2 * 100にしたら上手くいきました。 なぜ、上記コードだと上手くいかないんでしょうか?

  • C言語初心者です。次の問題で質問です。

    三つの整数値を読み込んで、その最小値を求めて表示するプログラムを作成せよ。 (※if文ではなく、条件演算子を用いよ) if文を用いてのプログラミングは出来ました。 しかし、条件演算子のプログラミングが行き詰りました…。 条件演算子の組み合わせ方が良く分かりません 以下、自分の解答です。(おそらく誤りです) #include <stdio.h> int main(void) { int n1,n2,n3,min; printf("三つの整数を入力してください。\n"); printf("整数1:"); scanf("%d",&n1); printf("整数2:"); scanf("%d",&n2); printf("整数3:"); scanf("%d",&n3); min = (n1>n2>n3) ? n2 : n3; /*ここが分かりません*/ printf("最小値は%dです。\n",min); return 0; } (n1>n2>n3) ? としてしまうと、大小関係の分別が複雑になるので、この書き方は間違っていると思います。(例として、(6>1>4)や(1>6>4)など。) (n1>n2 || n2>n3) ? などと書くのでしょうか?分かりません。 回答お願いいたします。

  • use bigintを使っている時に、float の計算はできますか?

    少し前に、perlで64ビット整数を扱う方法を質問し、use bigint というのをお教えいただきました者です。それはうまくいったのですが、どうも float の計算がうまくいかなくなったようです。use bigint をコメントアウトすると、doubleで計算しているため、精度が悪いのですが、とにかく $a/$b などが計算できていました。しかし、use bigintを入れると $a $b は正確に計算できているのですが、 $a/$b は整数にしかなりません。こういうところは double の精度で十分なのですが、bigint と共存する方法はないでしょうか?

    • ベストアンサー
    • Perl
  • 4の倍数を論理演算で表す。。

    /*Prog32.cキーボードから整数型変数aへデータを入力し、aの値が4の倍数のときは“4の倍数です”と表示し、そうでないときは、“4の倍数ではありません”と表示する。ただし、整数データのビット数はわからないものとし、計算にはビットごとの論理演算のみを用いる。*/ #include <stdio.h> int main(void) { int a; printf("整数を入力して下さい! "); scanf("%d", &a); (a % 4 ) ? printf("4の倍数ではありません。\n", a) :printf("4の倍数です。\n", a); return(0); } 4の倍数を論理演算で表す方法がわかりません。。 自分では上記のように算術を使うやりかたしかわかりませんでした。 よろしくお願いします!

  • if文を条件演算子に書き換えについて・・・

    /* 読み込んだ三つの整数値の最大値を求めて表示 */ #include <stdio.h> int main(void) { int n1, n2, n3, max; puts("三つの整数を入力してください。"); printf("整数1:"); scanf("%d", &n1); printf("整数2:"); scanf("%d", &n2); printf("整数3:"); scanf("%d", &n3); max = n1; if (n2 > max) max = n2; if (n3 > max) max = n3; printf("最大値は%dです。\n", max); return (0); } 上記のソースコードを、条件演算子を用いた形にするという問題なのですが、初心者で理解しずらいところもありどうやっても同じ結果を出すことが出来ないため質問させていただきました。 ご教授お願いしますm(_)m

専門家に質問してみよう