非常に簡単な解決策は、まともなテーブル駆動型の近似を使用することです。入力を正しく削減すれば、実際には多くのデータは必要ありません。exp(a)==exp(a/2)*exp(a/2)
、つまり、実際に計算する必要があるのは だけexp(x)
です1 < x < 2
。その範囲で、runga-kutta 近似は ~16 エントリ IIRC で妥当な結果をもたらします。
同様に、sqrt(a) == 2 * sqrt(a/4) == sqrt(4*a) / 2
これは のテーブル エントリのみが必要であることを意味します1 < a < 4
。Log(a) は少し難しいです: log(a) == 1 + log(a/e)
. これはかなり遅い反復ですが、log(1024) はわずか 6.9 であるため、多くの反復はありません。
pow: にも同様の「整数優先」アルゴリズムを使用しますpow(x,y)==pow(x, floor(y)) * pow(x, frac(y))
。これpow(double, int)
は些細なこと (分割統治) であるため機能します。
[編集] の整数コンポーネントのlog(a)
場合、テーブルを格納すると便利な場合があるため、そのテーブル内の a の単純なハードコードされたバイナリ検索によって1, e, e^2, e^3, e^4, e^5, e^6, e^7
削減できます。log(a) == n + log(a/e^n)
7 ステップから 3 ステップへの改善はそれほど大きくありませんが、 で割るe^n
代わりに で1n
回割ればよいことを意味しますe
。
[編集 2] そして、その最後のlog(a/e^n)
項については、使用できます- 各反復は、テーブル ルックアップによってlog(a/e^n) = log((a/e^n)^8)/8
さらに 3 ビットを生成します。これにより、コードとテーブルのサイズが小さく保たれます。これは通常、組み込みシステム用のコードであり、大きなキャッシュはありません。
[編集 3] それは私の側ではまだ賢明ではありません。log(a) = log(2) + log(a/2)
. 固定小数点値を保存し、log2=0.6931471805599
先頭のゼロの数を数え、a
ルックアップ テーブルに使用される範囲にシフトし、そのシフト (整数) を固定小数点定数で乗算するだけlog2
です。命令数は 3 つまでです。
削減ステップに使用e
すると、「良い」log(e)=1.0
定数が得られますが、それは誤った最適化です。0.6931471805599 は 1.0 と同じくらい良い定数です。どちらも 10.22 固定小数点の 32 ビット定数です。範囲削減の定数として 2 を使用すると、除算にビット シフトを使用できます。
[編集 5] Q10.22 に保存しているので、log(65536)=11.09035488 の方が適切に保存できます。(16 x log(2))。「x16」は、さらに 4 ビットの精度が利用可能であることを意味します。
編集 2 からのトリックを引き続き取得しますlog(a/2^n) = log((a/2^n)^8)/8
。基本的に、これにより結果が得られます(a + b/8 + c/64 + d/512) * 0.6931471805599
-範囲[0,7]のb、c、d。a.bcd
実際には 8 進数です。パワーとして 8 を使用したので、驚くことではありません。(このトリックは、べき乗が 2、4、または 16 のいずれでも同じように機能します。)
[編集 4] まだオープンエンドがありました。pow(x, frac(y)
はちょうどpow(sqrt(x), 2 * frac(y))
であり、私たちはまともな1/sqrt(x)
. これにより、はるかに効率的なアプローチが可能になります。frac(y)=0.101
バイナリ、つまり 1/2 プラス 1/8 と言ってください。ということx^0.101
は、 です(x^1/2 * x^1/8)
。でも、ただいまx^1/2
です。もう 1 つの演算を保存すると、ニュートン ラフソンが与えるので、 を計算します。最終結果を反転するだけで、sqrt 関数を直接使用しないでください。sqrt(x)
x^1/8
(sqrt(sqrt(sqrt(x)))
NR(x)
1/sqrt(x)
1.0/(NR(x)*NR((NR(NR(x)))