
組み込み系で開発しているものです。
どうしても 実数のべき乗 を使わねばならないのですが、
powf があまりに遅くて話になりません。
条件
・sinf, cosf は使ってもよい(高速なライブラリがあるので)
・指数は符号なし整数のみ(0~10000)
・底は 0.0~1.0 のみ
・戻り値も 0.0~1.0 のみ
・精度は 16bit 位で十分
・計算しておいてテーブルから呼んでもよい
自作で powf を作りたいのですが、
3時間いろいろやってみてもうまくいきませんでした。
他のやり方は無いでしょうか?
No.7ベストアンサー
- 回答日時:
> 対象ハードは ADSP-BF539 です。
そのプロセッサはFPUがなかったのでは?
そうであれば、主に時間が掛かっているのは浮動小数点演算の部分かと思います(ソフトエミュレーションなので)。
というわけで、#2さんのアルゴリズムで、固定小数点演算を行えばかなり改善するはずです。
固定小数点演算は、加算はそのままで構いませんし、乗算は16ビット×16ビットで計算してから、適当に丸めて16ビット右シフトすればOKです。精度を上げるには、途中の計算を16ビットではなく20ビットにしてもよいでしょう(40ビット整数型が使えるはずなので)。
回答ありがとうございます。
>乗算は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;
}
No.11
- 回答日時:
> ちなみに inline をつけたら 562 に若干増えました???
命令キャッシュが小さいからでしょう。
また、データキャッシュのサイズを考えると、表引きが有効とは思えません。
最適化オプションをどうしているのか分かりませんが、速度を最適化するより、サイズを最適化する方が結果的に高速になる可能性があります。また、案外register指定子が効くことがありますので、これも試してみてください。
回答ありがとうございます。
register ですが、効果がありませんでした。
(シミュレータで擬似的に動かしているからかもしれません)
最後に。結果として powf より 12倍以上早く計算できました。
開発は現在の方針で進めようと思っています。
長々とお付き合いありがとうございました。
No.10
- 回答日時:
> まだ高速化できるでしょうか?
微々たるものでしょうが、nの型をunsigned shortではなくunsigned intにしてはどうでしょうか?
汎整数拡張によるオーバーヘッドがそれなりにありそうです。
あとは、for文ではなくdo文で書き直すとわずかに速くなります。
それ以上はアセンブリ言語に踏み込まないと難しそうです。
回答ありがとうございます。
>unsigned shortではなくunsigned int
消費サイクル数が 584 から 568 になりました。
>for文ではなくdo文で書き直す
消費サイクル数が 584 から 571 になりました。
2つを組み合わせて 584 から 555 になりました。
約 5% の高速化です。
ちなみに inline をつけたら 562 に若干増えました???
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;
}
回答ありがとうございます。
select_my_powf_table999 ですが、
この関数に引数として 底、指数 を持たせて、
0.999 以外の底のべき乗を返すことはできないでしょうか?
(つまり、0.999 のテーブルから 0.999 以外の底の値を取得したいのです)
No.8
- 回答日時:
>・計算しておいてテーブルから呼んでもよい
テーブルを使用する場合、
必要になるテーブル領域を単純に計算すると、
精度は16bit×0x4000×10000=327,680,000バイト
となりますから、
>ROM/RAM あわせて100KBくらいまで使えます。
の方法では、少し無理がありそうです。
回答ありがとうございます。
例えば 32768 位のテーブルを作ります。
0.999 を底に、指数 0~32767 まで計算しておきます。
このテーブルを元に、底と指数からテーブルのインデックスを決めて取り出す、
ということは可能でしょうか?
(散々計算しましたが、インデックスの位置の決め方が分からず、断念しました)
No.6
- 回答日時:
>#3さん
>(2)方法その2
>底や、指数の範囲も決まっているみたいですので、
>固定小数点で計算してから、浮動小数点に変換する方法も、よさそうですね。
>・底は 0.0~1.0 のみ
という情報だけからは、底(の範囲)が決まっているとは言えなそうです。
きざみ幅などの情報がないからです。
0.9の場合があるかもしれませんし、0.99999999の場合があるかもしれません。
小数点以下の桁数がもっと多い場合のことも考える必要があるのかもしれません。
よって、固定小数点で計算するのはむずかしそうです。
範囲が決められないからです。
きざみ幅などの情報がないからです。
>ご指摘ありがとうございます。
刻み幅ですが、0.0~1.0 と書きましたが実際には
渡されるのは符号なし整数で 0x0000~0x4000 が範囲になります。
0x0000 を 0.0、0x4000 を 1.0 に直してから計算しています。
なので刻み幅は 1.0 / 0x4000 = 0.00006103515625 です。
No.5
- 回答日時:
#1です。
先ほどの補足要求の際に一緒にたずねるべきでしたが、まずはCPUとコンパイラを明確にしてください。メモリサイズに関する情報も必須です。
FPUの有無によっても、当然どうすべきかが変わってきます。
コンパイラは VisualDSP4.5 です。
対象ハードは ADSP-BF539 です。
http://www.analog.com/jp/embedded-processing-dsp …
ROM/RAM あわせて100KBくらいまで使えます。
現在は設計段階で、冶具もないために
VisualDSPのシミュレータでサイクル数を比較しています。
最終目標は作ろうとしているモジュールの MIPS の概算を出して、
ソフトウェアとして破綻しないことを提示することです。
(現在サイクル数からMIPSを計算しています。あくまで概算なので)
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;
}
No.3
- 回答日時:
(1)方法その1
>・計算しておいてテーブルから呼んでもよい
とあるので、そのように実装してみては、如何でしょう。
速度的には速くなると思いますよ。
(2)方法その2
底や、指数の範囲も決まっているみたいですので、
固定小数点で計算してから、浮動小数点に変換する方法も、よさそうですね。
こちらの方法だと、(1)よりは若干遅くなりますが、消費メモリを大幅に
抑えられそうです。
一番早いのは多分テーブルから呼び出すことだと思いますが、
テーブルのどこから持ってくればいいのかがわかりません。
実際に表を組んで見ました。
底 0.95 で大体1になるのが 150乗 として テーブルサイズ 150 で、
あらかじめ表を作ってみました。
指数をインデックスとして、底 0.95、指数 35 が渡された場合、
テーブルの 35 番目を返します。
では、底 0.837、指数 23 だった場合、
どのインデックスを参照するればよいのでしょうか?
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のべき乗数の和で表わすと、
劇的とはいかないかもしれませんが、単純なループで掛け算を繰り返すよりは
いくぶんかでも効率がよくなるかもしれません。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- その他(ソフトウェア) F-BASICで計算中の実行が中途で勝手に止まり、大変困っています。 2 2023/03/02 16:15
- Visual Basic(VBA) Excel のユーザー定義関数でソルバーが動作しない 1 2022/09/05 19:51
- SoftBank(ソフトバンク) ソフトバンク(SoftBank)でのモバイルデータ通信量の計算と、iPhone内でのモバイルデータ通 2 2022/08/08 13:33
- 会社・職場 Excel、Googleスプレッドシートなどへのデータ入力 2 2023/07/06 05:59
- その他(法律) 役所の人間が勝手に間違えて振込、それを知って使って刑事事件とか、不当逮捕そのものでは? 5 2022/05/22 15:13
- その他(プログラミング・Web制作) COBOL計算 2 2022/11/18 14:35
- 統計学 どの統計を使えばいいのか教えてください(EZ-Rを使用) 5 2022/10/11 13:28
- C言語・C++・C# numpyスライス機能を使った数値計算 2 2023/05/08 16:01
- その他(お金・保険・資産運用) 至急!【Wolt】各メニューの価格設定の簡単な計算方法 3 2023/03/05 11:58
- 数学 『最後の自然数はどんな数か』 3 2023/06/26 20:38
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
三菱シーケンサ(Aシリーズ)で...
-
有効数字について 以前質問をし...
-
ExcelのINT関数の計算結果がお...
-
CRCの計算方法について
-
EXCELの関数"STDEV(標準偏差)"...
-
VB.net Double と...
-
O(n log n)について2
-
froat型
-
VBAでミリ秒まで出力する方法
-
c languageで 簡単な質問があ...
-
大きすぎる数値になるとE+にな...
-
floatの有効桁数
-
時刻の比較
-
Visual Studioのバージョンによ...
-
float型の精度(有効桁と実数)...
-
計算の丸め誤差の解消について
-
4096bitを95種類で表現すると何...
-
計算が合わない
-
どんな数字を入力してもaverage...
-
ExcelでPC(パソコン)によって...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ExcelでPC(パソコン)によって...
-
有効数字について 以前質問をし...
-
O(n log n)について2
-
VB.net Double と...
-
ExcelのINT関数の計算結果がお...
-
c languageで 簡単な質問があ...
-
VBAでミリ秒まで出力する方法
-
EXCELの関数"STDEV(標準偏差)"...
-
16進数 加算 減算 C言語
-
三菱シーケンサ(Aシリーズ)で...
-
VB6.0での小数点の扱いについて
-
除算を使わずに10で割りたい。
-
”/”を使わずに割り算したいんで...
-
計算の丸め誤差の解消について
-
CRCの計算方法について
-
2038年問題 日付算出
-
VB6のFIX関数での誤差について
-
色の判定
-
大きすぎる数値になるとE+にな...
-
浮動小数演算は実行環境の変化...
おすすめ情報