• ベストアンサー

powf を使わずにべき乗を計算

組み込み系で開発しているものです。 どうしても 実数のべき乗 を使わねばならないのですが、 powf があまりに遅くて話になりません。 条件 ・sinf, cosf は使ってもよい(高速なライブラリがあるので) ・指数は符号なし整数のみ(0~10000) ・底は 0.0~1.0 のみ ・戻り値も 0.0~1.0 のみ ・精度は 16bit 位で十分 ・計算しておいてテーブルから呼んでもよい 自作で powf を作りたいのですが、 3時間いろいろやってみてもうまくいきませんでした。 他のやり方は無いでしょうか?

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

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.7

> 対象ハードは ADSP-BF539 です。 そのプロセッサはFPUがなかったのでは? そうであれば、主に時間が掛かっているのは浮動小数点演算の部分かと思います(ソフトエミュレーションなので)。 というわけで、#2さんのアルゴリズムで、固定小数点演算を行えばかなり改善するはずです。 固定小数点演算は、加算はそのままで構いませんし、乗算は16ビット×16ビットで計算してから、適当に丸めて16ビット右シフトすればOKです。精度を上げるには、途中の計算を16ビットではなく20ビットにしてもよいでしょう(40ビット整数型が使えるはずなので)。

postal0x02
質問者

お礼

回答ありがとうございます。 >乗算は16ビット×16ビットで計算してから、適当に丸めて16ビット右シフト asuncion さんの解法と組み合わせると、 powf の 12~13倍 は早くなりました。 精度は小数第4位くらいまでなら大体あっています。 まだ高速化できるでしょうか? // 0.0~1.0 を 0~65536 として計算する。 unsigned int powf2(unsigned int f, unsigned short n) {   unsigned int ret=65536;   if(f==0) return 0;   if(n==0) return 65536;   if(n==1) return f;   for(;n;n>>=1)   {     if(n & 1)     {       ret *= f;       ret >>=16;     }     f *= f;     f >>= 16;   }   return ret; }

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (10)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.11

> ちなみに inline をつけたら 562 に若干増えました??? 命令キャッシュが小さいからでしょう。 また、データキャッシュのサイズを考えると、表引きが有効とは思えません。 最適化オプションをどうしているのか分かりませんが、速度を最適化するより、サイズを最適化する方が結果的に高速になる可能性があります。また、案外register指定子が効くことがありますので、これも試してみてください。

postal0x02
質問者

お礼

回答ありがとうございます。 register ですが、効果がありませんでした。 (シミュレータで擬似的に動かしているからかもしれません) 最後に。結果として powf より 12倍以上早く計算できました。 開発は現在の方針で進めようと思っています。 長々とお付き合いありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.10

> まだ高速化できるでしょうか? 微々たるものでしょうが、nの型をunsigned shortではなくunsigned intにしてはどうでしょうか? 汎整数拡張によるオーバーヘッドがそれなりにありそうです。 あとは、for文ではなくdo文で書き直すとわずかに速くなります。 それ以上はアセンブリ言語に踏み込まないと難しそうです。

postal0x02
質問者

お礼

回答ありがとうございます。 >unsigned shortではなくunsigned int 消費サイクル数が 584 から 568 になりました。 >for文ではなくdo文で書き直す 消費サイクル数が 584 から 571 になりました。 2つを組み合わせて 584 から 555 になりました。 約 5% の高速化です。 ちなみに inline をつけたら 562 に若干増えました???

全文を見る
すると、全ての回答が全文表示されます。
  • Yanch
  • ベストアンサー率50% (114/225)
回答No.9

>例えば 32768 位のテーブルを作ります。 >0.999 を底に、指数 0~32767 まで計算しておきます。 >このテーブルを元に、底と指数からテーブルのインデックスを決めて取り出す、 >ということは可能でしょうか? >(散々計算しましたが、インデックスの位置の決め方が分からず、断念しました) テーブルを使用する場合の例です。 実際に実装する場合は、init関数を使用せずに、 constでテーブルを定義しておくとよいです。 ---------------------------------------------------------------------- #include <stdio.h> #include <math.h> #define MYPOWF_TABLE_SIZE 32768 double g_mypowf_table999[MYPOWF_TABLE_SIZE]; void init_mypowf_table999() {   int y;      for (y = 0; y < MYPOWF_TABLE_SIZE; y++)   {     g_mypowf_table999[y] = powf(0.999, y);   } } double select_my_powf_table999(int y) {   return g_mypowf_table999[y]; } int main(int argc, char *argv[]) {   double z;   int y;      /* テーブルを初期化 */   init_mypowf_table999();      for (y = 0; y < 10; y++)   {     /* y:指数 */     z = select_my_powf_table999(y);     printf("select_my_powf_table999(%d){%.15lf}\n", y, z);   }      return 0; }

postal0x02
質問者

お礼

回答ありがとうございます。 select_my_powf_table999 ですが、 この関数に引数として 底、指数 を持たせて、 0.999 以外の底のべき乗を返すことはできないでしょうか? (つまり、0.999 のテーブルから 0.999 以外の底の値を取得したいのです)

全文を見る
すると、全ての回答が全文表示されます。
  • Yanch
  • ベストアンサー率50% (114/225)
回答No.8

>・計算しておいてテーブルから呼んでもよい テーブルを使用する場合、 必要になるテーブル領域を単純に計算すると、 精度は16bit×0x4000×10000=327,680,000バイト となりますから、 >ROM/RAM あわせて100KBくらいまで使えます。 の方法では、少し無理がありそうです。

postal0x02
質問者

お礼

回答ありがとうございます。 例えば 32768 位のテーブルを作ります。 0.999 を底に、指数 0~32767 まで計算しておきます。 このテーブルを元に、底と指数からテーブルのインデックスを決めて取り出す、 ということは可能でしょうか? (散々計算しましたが、インデックスの位置の決め方が分からず、断念しました)

全文を見る
すると、全ての回答が全文表示されます。
  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.6

>#3さん >(2)方法その2 >底や、指数の範囲も決まっているみたいですので、 >固定小数点で計算してから、浮動小数点に変換する方法も、よさそうですね。 >・底は 0.0~1.0 のみ という情報だけからは、底(の範囲)が決まっているとは言えなそうです。 きざみ幅などの情報がないからです。 0.9の場合があるかもしれませんし、0.99999999の場合があるかもしれません。 小数点以下の桁数がもっと多い場合のことも考える必要があるのかもしれません。 よって、固定小数点で計算するのはむずかしそうです。 範囲が決められないからです。

postal0x02
質問者

お礼

きざみ幅などの情報がないからです。 >ご指摘ありがとうございます。 刻み幅ですが、0.0~1.0 と書きましたが実際には 渡されるのは符号なし整数で 0x0000~0x4000 が範囲になります。 0x0000 を 0.0、0x4000 を 1.0 に直してから計算しています。 なので刻み幅は 1.0 / 0x4000 = 0.00006103515625 です。

全文を見る
すると、全ての回答が全文表示されます。
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.5

#1です。 先ほどの補足要求の際に一緒にたずねるべきでしたが、まずはCPUとコンパイラを明確にしてください。メモリサイズに関する情報も必須です。 FPUの有無によっても、当然どうすべきかが変わってきます。

postal0x02
質問者

お礼

コンパイラは VisualDSP4.5 です。 対象ハードは ADSP-BF539 です。 http://www.analog.com/jp/embedded-processing-dsp/blackfin/processors/index.html ROM/RAM あわせて100KBくらいまで使えます。 現在は設計段階で、冶具もないために VisualDSPのシミュレータでサイクル数を比較しています。 最終目標は作ろうとしているモジュールの MIPS の概算を出して、 ソフトウェアとして破綻しないことを提示することです。 (現在サイクル数からMIPSを計算しています。あくまで概算なので)

全文を見る
すると、全ての回答が全文表示されます。
  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.4

どれくらい性能がよいかはわかりません。 #include <stdio.h> double mypowf(double a, int n) { double p; if (a == 0.0) return 0.0; if (a == 1.0) return 1.0; if (n == 1) return a; for (p = 1; n; n /= 2) { if (n & 1) { p *= a; } a *= a; } return p; } int main(void) { int i; for (i = 0; i < 33; ++i) { printf("%.15f\n", mypowf(.5, i)); } return 0; }

postal0x02
質問者

お礼

回答ありがとうございます。 powf より2倍早いです。

全文を見る
すると、全ての回答が全文表示されます。
  • Yanch
  • ベストアンサー率50% (114/225)
回答No.3

(1)方法その1 >・計算しておいてテーブルから呼んでもよい とあるので、そのように実装してみては、如何でしょう。 速度的には速くなると思いますよ。 (2)方法その2 底や、指数の範囲も決まっているみたいですので、 固定小数点で計算してから、浮動小数点に変換する方法も、よさそうですね。 こちらの方法だと、(1)よりは若干遅くなりますが、消費メモリを大幅に 抑えられそうです。

postal0x02
質問者

お礼

一番早いのは多分テーブルから呼び出すことだと思いますが、 テーブルのどこから持ってくればいいのかがわかりません。 実際に表を組んで見ました。 底 0.95 で大体1になるのが 150乗 として テーブルサイズ 150 で、 あらかじめ表を作ってみました。 指数をインデックスとして、底 0.95、指数 35 が渡された場合、 テーブルの 35 番目を返します。 では、底 0.837、指数 23 だった場合、 どのインデックスを参照するればよいのでしょうか?

全文を見る
すると、全ての回答が全文表示されます。
  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.2

>・指数は符号なし整数のみ(0~10000) この条件から、「指数の回数」だけ掛け算を繰り返すのがベースになろうかと思います。 ただし、単純に繰り返すのではなく、例えば指数が16ならば (((底の2乗)の2乗)の2乗)の2乗 とすることで、掛け算の回数を減らすことができます。 この考え方を拡張して、指数が2のべき乗(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192)の 場合を記憶しておきます。 そして、1~10000の指数(0乗は自明なので省略)を2のべき乗数の和で表わすと、 劇的とはいかないかもしれませんが、単純なループで掛け算を繰り返すよりは いくぶんかでも効率がよくなるかもしれません。

postal0x02
質問者

お礼

回答ありがとうございます。 組んでみましたが、powf より2倍は早い結果を得られました。

全文を見る
すると、全ての回答が全文表示されます。
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.1

> 3時間いろいろやってみてもうまくいきませんでした。 > 他のやり方は無いでしょうか? 他のやり方といわれても、まずはそのうまくいかなかったものを出していただかないとどうしようもありません。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • べき乗微分の指数の拡張

    高校2年生です。 ひょっとしたら高校レベルではないかもしれませんが、少し気になったので質問します。 一般にべき乗微分は (x^n)'=(x^n-1) で表されます。ずっとnは自然数のときのみかと思っていましたが、どうもnは実数の範囲に拡張できるようなのです。 調べてみましたら、指数が負の整数のときについては積の微分公式を使用して証明できましたが、指数が分数のときが、なぜそうなるのか理解できません。 どなたか高校生でも分かるように説明していただけないでしょうか。 またこれは関係ないのですが、反比例の式を原点から定積分するとどうなるのでしょうか。 反比例の式は最大値がありませんが、面積(定積分)はどうなるのでしょうか。

  • IEEE754の項でべき乗記号を省略する理由は

    応用情報技術者試験の過去問で、IEEE754による単精度の浮動小数点表示法が次のように記載されています。 ----------------------------------------------------------------------- 〔IEEE 754〕 0 < E < 255のとき表示される実数  (-1) S × 2E - 127 × (1 + F) ここで、Sは実数の符号(0:正、1:負)     Eはげたばき(バイアス付き)の指数     Fは純小数 これらS,E,Fの2進数表示を並べて元の数を表す。 例えば、2進数(0.011) 2は、(-1) 0 × 2125 - 127 × (1 + 0.1) 2なので、 S = 0、E = 125、F = (0.1) 2となる。ここで、()2内の数は2進数を表す。 ----------------------------------------------------------------------- 私の拙い数学知識で解釈すると、 -1×S×2×E-127×(1+F) このようになるのですが。 本来は (-1)^S×2^(E-127)×(1+F) こういう意味のようです。 暗黙の了解のように、べき乗記号が省かれ、 先に計算すべき(E-127)の括弧まで省かれているのですが、 なぜそのような表記になっているんでしょうか。

  • 浮動小数点表現

    2^{24}を32bit整数表現及び32bit(単精度)浮動小数点表現で表せ。 結果は16進数で示せ。 符号ビット:MSB 指数部n:7ビット 仮数部:24ビット という問題があるのですが、 解いてみたものの、答えもないのであっているのか分かりません。 以下の答えで合っているでしょうか? また、合っていなかったら、どのように解くのか教えていただけませんか? 整数表現 0100 0000H 浮動小数点 0100 1000 0100 0000 0000 0000 0000 0000

  • 単精度実数の範囲を求める方法を教えて下さい

    ご教授願います。 単精度実数では1ビットの符号部、7ビットの指数部、24ビットの仮数部で表し、-3.40282×10^38~3.40282×10^38の範囲を表すとあります。この、-3.40282×10^38~3.40282×10^38はどのように計算すると求められるのでしょうか? よろしくお願い致します。

  • SSEを利用した差分の総和の求め方

    現在、できるだけ速く、距離計算を行いたいと考えています。 具体的には、8bit符号なし整数列の距離計算です。 SSEを利用するのが速いので、 _mm_sad_epu8()を使って組みました。 確かに高速に動作しておりますが、 本当はSAD(絶対誤差の総和)ではなく、SSD(2乗誤差の総和)を求めたいのです。 8bitの符号なし整数列を32bitの符号付き整数列に置き換えてから、 計算するのだとは思いますが、どのように組めば効率が良いのか分かりません。 計算の流れは、 ・8bit→32bit ・32bit整数列の差分 ・32bit整数列の乗算(2乗) ・32bit整数列の総和 となると思いますが、 8bit→32bitを行う関数も見当たらないですし、 符号付き32bit整数列の乗算関数も見当たりません。 どなたかご存知であれば、教えていただけると幸いです。

  • 浮動小数点とIEEE754

    初めまして。 現在、浮動小数点について調べています。 #質問カテゴリ、間違っていたらすみません。 色々調べていると、こんなサイトを見つけました。 http://www.jtw.zaq.ne.jp/kayakaya/new/kihon/text/fudo.htm 分かり易い解説だったんですが、疑問も湧いてきました。 サイトの内容は、0.375を浮動小数点に直すという内容なんですが 汎用型の時は『0.11 × 2の-1乗』 IEEE754の時は『1.1 × 2の-2乗』 で計算しています。 Q1) 汎用とIEEEの違いは『127』を足すだけだと思っていたのですが 『2の-n乗』の直し方も異なるのでしょうか。 もう一つ質問です。 整数入力をIEEE754に準拠したフォーマットで出力させるデジタル回路を作成しています。 出力形式は32bit or 64bitです。(単精度か倍精度) そこで悩んでいるのが入力のbit数とその内訳です。 入力bitは『符号・整数・小数』で形成されているかと思うのですが 符号以外のbit内訳が分かりません。 Q2) 単精度/倍精度、それぞれの入力範囲bitと、その内訳を教えて頂けないでしょうか。 (単精度:合計16bit ⇒ 符号1bit、整数7bit、小数8bit)みたいな感じでお願いします。 宜しくお願いします。 分かりにくい質問でしたらご指摘下さい。

  • IEEE754 浮動小数点の問題

    -10.375(10進数)をIEEE754規格の単精度浮動小数点表現のビット列で示せ。という問題で、 ・仮数部の符号ビットが1bitで指数部が8bitで仮数部が23bitで合計32bitでいいんですよね?本によって割り当てが違うんですけど。 ・僕自身この問題を解いた結果、1 10000010 0100110・・・0 (一番前が、符号bit。真中が指数bit。一番後ろが仮数bit) で合っていますか?答え合わせのほどを。 どうか、願いします。

  • -5.75をIEEE754単精度実数表現

    -5.75(10進数)をIEEE754単精度実数表現する問題なんですが、 ・負だから符号ビット(1)…(1) ・5.75=(101.11000…)→1.0111000…×2^2 ・2=b-127 , b=129=(10000001)…(2) ・5.75を2bitで表したやつの小数点以下(0111000…)…(3) (1)(2)(3)を順に並べて『1 10000001 0111000…』 で合っていますか?よろしくお願いします。

  • 大きな数、大きな演算精度の実数をあつかえるクラス

    1000桁くらいの大きな整数とか、有効数字1000桁くらいの実数の演算が可能なクラスを探しています。十進BASICならできると聞いたので、ダウンロードして試してみてできることは確認しましたが、BASICは20数年ぶりなので、ほとんど忘れています。CかC++で同じような精度の演算ができるライブラリはないでしょうか?

  • 浮動小数点(エクセス64形式)、倍精度(64bit)仮数部は何bit?

    タイトルの通り、エクセス64形式の倍精度(64bit)のときの指数部、仮数部がそれぞれ何bitなのかわかりません。IEEE形式のものと、単精度(32bit)のものは調べられたのですが・・・ (421C 1999 9999 99A0) 等、いくつかエクセス64形式で表記された値があり、これを実数でいくつになるか確認するためです。 よろしくお願いします。