
高速逆平方根(Fast InvSqrt()または16 進定数0x5F3759DFとも呼ばれる)は、逆平方根を推定するアルゴリズムです。32ビット浮動小数点数の平方根の逆数(または乗法逆数)IEEE 754 浮動小数点形式で。このアルゴリズムは、1999 年にQuake III Arenaに実装されたことで最もよく知られています。その後のハードウェアの進歩、特にx86 SSE命令によりrsqrtss、このアルゴリズムは現代のコンピュータには一般的に最適な選択肢ではありませんが[ 1 ]、興味深い歴史的例として残っています[ 2 ] 。
このアルゴリズムは、32ビット浮動小数点数を入力として受け取り、半分の値を後で使用するために保存します。次に、浮動小数点数を表すビットを32ビット整数として扱い、 1ビット右に論理シフトを実行し、その結果を数値0x5F3759DFから減算します。これは、近似値の浮動小数点表現です。[ 3 ]これにより、入力の逆平方根の初期推定値が得られます。ビットを再び浮動小数点数として扱い、ニュートン法を1回反復実行して、より正確な近似値を得ます。
バークレーのウィリアム・カハンとKC・ングは、1986年5月に未発表の論文で、ビット操作技術とニュートン反復法を用いて平方根を計算する方法を説明した。[ 4 ] 1980年代後半、アーデント・コンピュータのクリーブ・モーラーはこの技術を知り[ 5 ]、同僚のグレッグ・ウォルシュに伝えた。グレッグ・ウォルシュは、現在では有名な定数かつ高速な逆平方根アルゴリズムを考案した。ゲイリー・タロリは当時アーデントに資金提供していたクボタのコンサルタントを務めており、おそらく1994年頃にこのアルゴリズムを3dfx Interactiveに持ち込んだと思われる。 [ 6 ] [ 7 ]
ジム・ブリンは、 1997年のIEEE Computer Graphics and Applications誌のコラムで、逆平方根の簡単な近似法を実証した。[ 8 ]他の同時代の3Dビデオゲームのリバースエンジニアリングにより、 Activisionの1997年のInterstate '76でこのアルゴリズムのバリエーションが発見された。[ 9 ]
一人称視点シューティングゲームであるQuake III Arenaは、 1999年にid Softwareからリリースされ、このアルゴリズムが使用されました。Brian Hookが3dfxからid Softwareにこのアルゴリズムを持ち込んだ可能性があります。 [ 6 ]このコードに関する議論は、2000年に中国の開発者フォーラムCSDNに登場し、 [ 10 ] Usenetとgamedev.netフォーラムは2002年と2003年にこのコードを広く拡散しました。 [ 11 ]誰がこのアルゴリズムを書いたのか、定数がどのように導出されたのかについて憶測が飛び交い、 John Carmackだと推測する人もいました。 [ 7 ] Quake IIIの完全なソースコードはQuakeCon 2005で公開されましたが、答えは得られませんでした。作者に関する疑問は、 SlashdotでBeyond3Dの憶測が人気になった後、オリジナル作者のGreg WalshがBeyond3Dに連絡したことで、2006年に解決されました。 [ 6 ]
2007年に、このアルゴリズムはフィールドプログラマブルゲートアレイ(FPGA)を使用した専用ハードウェア頂点シェーダーに実装されました。[ 12 ] [ 13 ]


コンピュータグラフィックスでは、入射角や反射角を計算するために、変換、クリッピング、ライティング、シェーディングに逆平方根が頻繁に使用されます。Quake III: Arena などのプログラムは、ライティングをシミュレートするために、毎秒多数のこれらの計算を実行する必要があります。[ 3 ]特に、ベクトルの長さを1にスケーリングして単位ベクトルを生成する操作では、逆平方根が使用されます。[ 14 ]
具体的には、ベクトルの長さは、そのユークリッドノルム(ベクトル成分の二乗和の平方根)を計算することによって決定されます。ベクトルの各成分をその長さで割ると、新しいベクトルは同じ方向を向いた単位ベクトルになります。3Dグラフィックスプログラムでは、すべてのベクトルは3次元空間にあるため、ベクトルになります。 それから、
はベクトルのユークリッドノルムであり、正規化された(単位)ベクトルは
ここで、分数項は逆平方根である。。
1990年代初頭に高速逆平方根アルゴリズムが開発された当時、ほとんどの浮動小数点処理能力は整数処理の速度に遅れをとっていた。[ 7 ]特に、当時のx87命令セットは、現代のSSE演算に比べて非常に低速だった。[ 1 ]高速逆平方根は、浮動小数点数の整数形式を加算および減算し、2で割ることによって平方根を取る(これは右シフトにすぎない)ことで、整数演算を通して良好な近似値を生成する。[ 3 ]
その後、ハードウェアメーカーによる追加機能により、このアルゴリズムはほとんどの場合不要になりました。たとえば、x86では、Intel は1999 年にSSE命令を導入しましたrsqrtss。2009 年にIntel Core 2で行われたベンチマークでは、この命令は高速逆平方根アルゴリズムの 3.54 ns と比較して、浮動小数点数あたり 0.85 ns で済み、誤差も少なくなりました。[ 1 ]
以下のCコードは、 Quake III Arenaの高速逆平方根実装であり、Cプリプロセッサディレクティブは削除されていますが、元のコメントテキストはそのまま含まれています。[ 15 ]
float Q_rsqrt ( float number ) { long i ; float x2 , y ; const float threehalfs = 1.5F ;x2 = number * 0.5F ; y = number ; i = * ( long * ) & y ; // 悪質な浮動小数点ビットレベルのハッキングi = 0x5f3759df - ( i >> 1 ); // なんだこれ? y = * ( float * ) & i ; y = y * ( threehalfs - ( x2 * y * y ) ); // 1回目の反復// y = y * ( threehalfs - ( x2 * y * y ) ); // 2回目の反復、これは削除可能return y ; }当時、逆平方根を計算する一般的な方法は、近似値を計算することでした。そして、実際の結果の許容誤差範囲内に収まるまで、別の方法でその近似値を修正します。1990年代初頭の一般的なソフトウェア手法では、ルックアップ テーブルから近似値を取得していました。[ 16 ]高速逆平方根の鍵は、浮動小数点数の構造を利用して近似値を直接計算することであり、テーブル参照よりも高速であることが証明されました。このアルゴリズムは、別の方法で平方根を計算し、浮動小数点除算で逆数を計算するよりも約 4 倍高速でした。[ 17 ]このアルゴリズムはIEEE 754-1985 32 ビット浮動小数点仕様を念頭に置いて設計されましたが、Chris Lomont の調査により、他の浮動小数点仕様でも実装できることが示されました。[ 18 ]
高速逆平方根トリックによって得られる速度上の利点は、32 ビット浮動小数点ワード [ 注 1 ] を整数として扱い、それをマジック定数 0x 5F3759DFから減算することによって得られます。[ 7 ] [ 19 ] [ 20 ] [ 21 ]この整数減算とビットシフトの結果得られるビットパターンは、浮動小数点数として再定義すると、その数の逆平方根の概算値になります。精度を上げるためにニュートン法を 1 回反復実行し、コードが完成します。このアルゴリズムは、ニュートン法の独自の第 1 近似を使用して、かなり正確な結果を生成しますが、1999 年にリリースされた x86 プロセッサのSSE命令を使用するよりもはるかに遅く、精度も低くなります。 [ 1 ] [ 22 ]rsqrtss
例えば、その数字は計算に使用できますアルゴリズムの最初のステップを以下に示します。
0011_1110_0010_0000_0000_0000_0000_0000 xとiの両方のビットパターン 0001_1111_0001_0000_0000_0000_0000_0000 右に1つシフトします: (i > > 1) 0101_1111_0011_0111_0101_1001_1101_1111 魔法の数字 0x5F3759DF 0100_0000_0010_0111_0101_1001_1101_1111 0x5F3759DF の結果 - (i > > 1)
IEEE 32ビット表現として解釈します。
0_01111100_01000000000000000000000 1.25 × 2 −3 0_00111110_00100000000000000000000 1.125 × 2 −65 0_10111110_01101110101100111011111 1.432430... × 2 63 0_10000000_01001110101100111011111 1.307430... × 2 1
この最後のビットパターンを浮動小数点数として再解釈すると、近似値が得られます。これは約3.4%の誤差があります。ニュートン法を1回繰り返した後の最終結果は誤差はわずか0.17%です。
C 標準によれば、浮動小数点値をキャストしてそのポインタを逆参照することで整数として再解釈すると、指定されたアーキテクチャで整数と浮動小数点のサイズが一致しない場合に未定義の動作を引き起こす可能性があります。 [ 23 ]これは、C の共用体[ 24 ]やC++20の[ 25 ]などの代替の型変換技術を使用することで回避できます。std::bit_cast
アルゴリズムは計算します以下の手順を実行してください。
このアルゴリズムは単精度浮動小数点数のビットレベル表現に大きく依存しているため、ここではその表現について簡単に概説します。ゼロ以外の実数をエンコードするには単精度浮動小数点数として、最初のステップは正規化されたバイナリ数として: [ 26 ]
指数は整数であり、は仮数の二進数表現です。仮数の小数点前の1ビットは常に1なので、保存する必要はありません。この式は次のように書き換えることができます。
どこ手段、 それでこの形式から、3 つの符号なし整数が計算されます。[ 27 ]
したがって:そして。
これらのフィールドは、左から右に32ビットコンテナにパックされます。[ 28 ]
例として、もう一度数字を考えてみましょう。正規化収量:
したがって、符号なし整数フィールドは次の3つです。
これらのフィールドは、下図に示すように配置されます。

この数値はバイナリで次のように表されます。
また、このアルゴリズムは実数で動作するため、のみ定義されていますコードは、そして。
平方根を計算するために与えられた数値は、次のように書き換えることができます。
もしコンピュータや電卓を使わずに計算する場合、対数表と恒等式が役立つだろう。これはすべての基数に有効です高速逆平方根は、この恒等式と、float32 を整数にエイリアスするとその対数の近似値が得られるという事実に基づいています。その方法は次のとおりです。
もしは正の正規数です。
それから
そしてそれ以来右辺の対数は[ 29 ]で近似できる。
どこは近似を調整するために使用される自由パラメータです。たとえば、区間の両端で正確な結果が得られる一方、これは最適な近似値(誤差の一様ノルムの意味で最良の値)をもたらします。ただし、この値は後続のステップを考慮しないため、アルゴリズムでは使用されません。

したがって、近似値が存在する
浮動小数点ビットパターンの解釈整数として収量[注4 ]
すると、は、スケーリングおよびシフトされた区分的線形近似である。右の図に示すように。言い換えれば、近似値は
計算アイデンティティに基づいています
上記の対数の近似を両方に適用するとそして上記の式から、次の式が得られます。
したがって、は:
コードには次のように書かれています。
i = 0x5f3759df - ( i >> 1 );上記の最初の項は魔法の数字です
そこから推測できることは2番目の項は、は、ビットをシフトすることによって計算されます。右に1つ位置。[ 30 ]
番号は方程式の解である前のステップで得られた近似値は、関数の零点を見つける方法である根探索法を使用して改良できます。このアルゴリズムはニュートン法を使用します。近似値がある場合、のためにすると、より良い近似値が得られます計算するには、、 どこは、で[ 31 ]方程式に適用ニュートン法では
これはコードでは次のように書かれています。y=y*(threehalfs-(x2*y*y));
この手順を繰り返すことで、関数の出力()を次の反復の入力として、アルゴリズムは逆平方根に収束する。[ 32 ] Quake IIIエンジンの目的のために、1 つの反復のみが使用されました。2 番目の反復はコード内に残っていましたが、コメントアウトされました。[ 21 ]
魔法数の正確な値がどのように決定されたかは正確には分かっていません。クリス・ロモントは、魔法数を選択することで近似誤差を最小化する関数を開発しました。範囲にわたって。彼はまず、線形近似ステップの最適定数を0x5F37642Fと計算しました。これは0x5F3759DFに近い値ですが、この新しい定数はニュートン法の 1 回反復後にわずかに精度が低下しました。[ 33 ]ロモントは次に、ニュートン法の 1 回および 2 回反復後でも最適な定数を探し、すべての反復段階で元の値よりも精度が高い0x5F375A86 を見つけました。 [ 33 ]彼は最後に、元の定数の正確な値が導出によって選択されたのか、試行錯誤によって選択されたのかを尋ねました。[ 34 ] ロモントは、64 ビット IEEE754 サイズ型の double のマジック ナンバーは0x5FE6EC85E7DE30DAであると述べましたが、後に Matthew Robertson によって正確には0x5FE6EB50C7B537A9であることが示されました。[ 35 ]
Jan Kadlec は、単一のニュートン法反復における定数も調整することで相対誤差をさらに 2.7 倍に減らし、 [ 36 ]徹底的な探索の末に
conv.i = 0x5F1FFFF9 - ( conv.i >> 1 ) ; conv.f * = 0.703952253f * ( 2.38924456f - x * conv.f * conv.f ) ; return conv.f ;単精度浮動小数点数については、マジックナンバーを決定するための完全な数学的解析が利用可能になった。[ 37 ] [ 38 ]
オブジェクトの格納値は、次のいずれかの型を持つ lvalue 式によってのみアクセスされるものとする: [...]
共用体オブジェクトの内容を読み取るために使用されるメンバーが、オブジェクトに値を格納するために最後に使用されたメンバーと同じでない場合、値のオブジェクト表現の適切な部分は、6.2.6 で説明されているように、新しいタイプのオブジェクト表現として再解釈されます (
このプロセスは「型パンニング」と呼ばれることもあります
)。