ルックアップ テーブルの形式で float 除算の逆数を作成することは可能ですか (1/f -> 1*inv[f] など) ? どのようにそれを行うことができますか?一部とマスクとシフトをフロートに適用して、インデックスの形式にする必要があると思いますか? 正確にはどうですか?
3 に答える
次のようなおおよその逆数を推測できます。
int x = bit_cast<int>(f);
x = 0x7EEEEEEE - x;
float inv = bit_cast<float>(x);
私のテストでは、0x7EF19D07 の方がわずかに優れていました (2 つのニュートン ラフソン改良の効果を含めてテストしました)。
Newton-Raphson を使用して改善できます。
inv = inv * (2 - inv * f);
必要なだけ繰り返します。2 ~ 3 回繰り返すと、良い結果が得られます。
より良い初期近似
相対誤差を最小限に抑えるには:
- 0x7EF311C2 (洗練なし)
- 0x7EF311C3 (1 リファインメント)
- 0x7EF312AC (2 つの改良)
- 0x7EEEEBB3 (3 リファインメント)
1 と 2 の間の入力の絶対誤差を最小限に抑えるには (その範囲外では十分に機能しますが、最適ではない可能性があります)。
- 0x7EF504F3 (絞り込みなし)
- 0x7EF40D2F (1 リファインメント)
- 0x7EF39252 (2 つの改良)
3 つの改良ステップでは、初期近似は最大相対誤差にほとんど影響しません。0x7EEEEEEEE はうまく機能し、これ以上のものは見つかりませんでした。
1 つの方法は次のとおりです。
- 入力から符号、指数、仮数を抽出する
- 最も重要な仮数ビットのいくつかを使用して、テーブルで逆数を検索します
- 指数を否定し、仮数のスケールの変更を調整します
- 符号、指数、および仮数を再結合して出力を形成します
ステップ 2 では、使用するビット数を選択し、精度とテーブル サイズの間でトレードする必要があります。下位ビットを使用してテーブル エントリ間を補間することで、精度を高めることができます。
ステップ 3 では、入力仮数が (0.5, 1.0] の範囲にあり、その逆数が [1.0, 2.0] の範囲にあり、出力仮数を得るために再正規化が必要なため、調整が必要です。
これについてはコードを書きません。おそらく、見逃してしまうような、やや厄介なエッジ ケースがいくつかあるからです。
また、数値計算を含む方法も調査する必要があります。これにより、メモリ アクセスが遅い場合により良い結果が得られる可能性があります。最新の PC アーキテクチャでは、キャッシュ ミスは数十回の算術演算と同じくらい高くつく可能性があります。ウィキペディアは良い出発点のようです。そしてもちろん、何をするにしても、実際に FPU の除算演算よりも高速であることを確認するために測定します。
最小ステップが 0.01 のようなものである場合、テーブルから逆 f をサポートできます。各インデックスは 100 倍されるので、
table[1]----->1.0/0.01
table[3]----->1.0/0.03
table[105]--->1.0/1.05
...
table[10000]->1.0/100.0
10000 elements for a range of (0.00,100.00)
より高い精度が必要な場合は、より多くのラムが必要になります。
もう一つの例:
range................: 0.000 - 1000.000
minimum increments ..: 0.001
total element number.: 1 million
something like this: table[2343]=1.0/2.343
もう一つの例:
range................: 0.000000 - 1.000000
minimum increments ..: 0.000001
total element number.: 1 million
something like this: table[999999]=1.0/0.999999