ARMのソフトウェアラスタライザーで使用するために、Goldschmidt除算を使用してQ22.10で固定小数点の逆数を計算しています。
これは、分子を1に設定するだけで実行されます。つまり、分子は最初の反復でスカラーになります。正直なところ、私はここでウィキペディアのアルゴリズムを盲目的にフォローしています。この記事によると、分母がハーフオープン範囲(0.5、1.0]でスケーリングされている場合、最初の適切な推定値は分母のみに基づくことができます。Fを推定スカラー、Dを分母とすると、F =2- D。
しかし、これを行うと、私は多くの精度を失います。512.00002fの逆数を見つけたい場合は言います。数値を縮小するために、シフトアウトされた小数部の精度が10ビット失われます。だから、私の質問は次のとおりです。
- 正規化を必要としないより良い見積もりを選択する方法はありますか?なんで?なぜだめですか?これが可能であるか不可能であるかについての数学的証明は素晴らしいでしょう。
- また、級数がより速く収束するように、最初の推定値を事前に計算することは可能ですか?現在、平均して4回目の反復後に収束します。ARMでは、これは約50サイクルの最悪のケースであり、clz/bsrのエミュレーションもメモリルックアップも考慮されていません。可能であれば、そうすることでエラーが増えるかどうか、そしてどれだけ増えるかを知りたいです。
これが私のテストケースです。注:clz
オンライン13のソフトウェア実装は、私の投稿からのものです。必要に応じて、組み込みに置き換えることができます。clz
先行ゼロの数を返し、値0の場合は32を返す必要があります。
#include <stdio.h>
#include <stdint.h>
const unsigned int BASE = 22ULL;
static unsigned int divfp(unsigned int val, int* iter)
{
/* Numerator, denominator, estimate scalar and previous denominator */
unsigned long long N,D,F, DPREV;
int bitpos;
*iter = 1;
D = val;
/* Get the shift amount + is right-shift, - is left-shift. */
bitpos = 31 - clz(val) - BASE;
/* Normalize into the half-range (0.5, 1.0] */
if(0 < bitpos)
D >>= bitpos;
else
D <<= (-bitpos);
/* (FNi / FDi) == (FN(i+1) / FD(i+1)) */
/* F = 2 - D */
F = (2ULL<<BASE) - D;
/* N = F for the first iteration, because the numerator is simply 1.
So don't waste a 64-bit UMULL on a multiply with 1 */
N = F;
D = ((unsigned long long)D*F)>>BASE;
while(1){
DPREV = D;
F = (2<<(BASE)) - D;
D = ((unsigned long long)D*F)>>BASE;
/* Bail when we get the same value for two denominators in a row.
This means that the error is too small to make any further progress. */
if(D == DPREV)
break;
N = ((unsigned long long)N*F)>>BASE;
*iter = *iter + 1;
}
if(0 < bitpos)
N >>= bitpos;
else
N <<= (-bitpos);
return N;
}
int main(int argc, char* argv[])
{
double fv, fa;
int iter;
unsigned int D, result;
sscanf(argv[1], "%lf", &fv);
D = fv*(double)(1<<BASE);
result = divfp(D, &iter);
fa = (double)result / (double)(1UL << BASE);
printf("Value: %8.8lf 1/value: %8.8lf FP value: 0x%.8X\n", fv, fa, result);
printf("iteration: %d\n",iter);
return 0;
}