Quake IIIの高速逆平方根(0x5f3759df)
1/√xを割り算も平方根も使わず一撃で近似する伝説のコードを、なぜ動くかまで解剖。IEEE754のビット表現が対数として振る舞う理屈と魔法定数の正体、作者の謎、いまの立ち位置を持ち帰れる。
- floatのビット列を整数と見なし、0x5f3759df から右1ビットシフトを引くだけで 1/√x の初期値を得る。IEEE754の指数部が対数近似として働くため、この整数減算が『対数を半分にして符号反転』に相当する。
- 初期値は相対誤差3%強だが、ニュートン法を1回回すと約0.175%まで収束する。定数は Chris Lomont の解析で 0x5f375a86 がわずかに優秀と判明したが、実用差はほぼない。
- 作者はJohn Carmackではない。Kahan/Ngの未公開ノートからCleve Moler、定数を考案したGreg Walsh、Gary Tarolliへ伝わり、SSEのrsqrtss登場で実務上の役目を終えた。
何のコードか
3D描画ではベクトルを長さ1に揃える「正規化」が絶え間なく必要で、各成分を長さ sqrt(x*x+y*y+z*z) で割ります。素直に書くと平方根と除算という当時のCPUで最も重い演算が要り、1フレームに何万回も走ります。Quake III Arena(1999年)のソースはこれを驚くべき方法で潰しました。それが Q_rsqrt、いわゆる高速逆平方根(fast inverse square root)です。有名になった理由は3つ。平方根も除算も使わず整数減算とビットシフトと少数の乗算だけで 1/sqrt(x) を誤差0.2%未満で返すこと、唐突な魔法定数 0x5f3759df とその投げやりなコメント、そしてこれほど有名なのに長年「誰が書いたのか」が不明だったことです。
コードと仕組みの解説
横にスクロール
公開されたコードはほぼ次のとおりです(コメントも原文どおり)。
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
肝は2行です。i = *(long*)&y は float のビット列を数値変換せず生の32ビット整数として読み替えます。次の 0x5f3759df - (i >> 1) が魔法で、鍵は IEEE754 の内部表現が対数の近似になっている点です。正の float は 符号(1) | 指数E(8) | 仮数M(23) と並び、値は概ね (1 + m) * 2^(E-127) です。このビット列 I を実数と見ると log2(値) はおおよそ (I / 2^23) - 127 に比例し、整数として読んだビット列はその数の2を底とする対数にほぼ線形対応 します。逆平方根の対数は log2(1/sqrt(x)) = -0.5 * log2(x) なので、対数の世界では「半分にして符号反転」で答えになります。i >> 1 が対数を半分にし、定数減算が符号反転と対数近似のずれ(オフセット)補正をまとめて担い、その最適オフセットをエンコードした値が 0x5f3759df です。それを y = *(float*)&i で float に読み戻します。
残る1行 y = y * (threehalfs - x2*y*y) はニュートン法の1ステップです。f(y) = 1/y^2 - x の零点を探すと反復式は y_next = y * (1.5 - 0.5*x*y*y) となり、コードと一致します。初期値の相対誤差は最大3%強ですが、2次収束するこの反復を1回通すと約0.175%へ一気に縮みます。コメントアウトされた2行目を活かせば誤差はさらに桁で下がりますが、ゲームには1回で十分と判断されました。
0x5f3759df は「指数バイアス補正 × 3/2」と「対数近似の直線が真の曲線からずれる分の最適オフセット」を1整数に畳んだ値で、厳密な手計算ではなく近似式の最小化で選ばれています。Chris Lomont は2003年、ニュートン法1回まで含め誤差最小の定数 0x5f375a86 を導きましたが差は約0.175%対0.175%で実用差はほぼありません。なお *(long*)&y の type punning は strict aliasing 規則の下では未定義動作で、現代では memcpy か(C++20の)std::bit_cast を使うべきです。
逸話と経緯
長らく作者は id の John Carmack だと噂されましたが本人ではありません。2005年のソース公開でも由来は明かされず、Beyond3D の Rys Sommefeldt が数年かけて出所をたどりました。判明した系譜はおおむね次のとおりです。
| 時期 | 人物・場所 | 貢献 |
|---|---|---|
| 1986年 | William Kahan / K.C. Ng | ビット操作+ニュートン法で平方根系を近似する手法を未公開ノートに記述 |
| 1980年代後半 | Cleve Moler(Ardent Computer) | この手法を知り同僚 Greg Walsh に伝える |
| 1980年代後半 | Greg Walsh(Ardent) | 有名な定数と逆平方根の形にまとめた実質的な作者 |
| 1994年ごろ | Gary Tarolli(3dfxへ) | この技法を業界(3dfx Interactive など)に伝播させたとされる |
| 1999年 | id Software(Quake III) | ゲームエンジンに採用。2005年のソース公開で一躍有名に |
決定打は2006年です。Slashdot で話題化した調査を見た Greg Walsh 本人が Beyond3D に連絡し、自分がこの定数とアルゴリズムを考案したと認めました。あの2つのコメントを書いたのは Walsh ではなく id 側の誰か(Terje Mathisen や Michael Abrash の名が挙がる)とされますが確証はありません。要は「Carmackの魔法」という俗説は誤りで、1980年代のワークステーション文化で磨かれた技法が巡り巡ってゲームエンジンに載っていた、というのが史実です。
遺産と教訓
皮肉にも、有名になった2005年には実務的な役目はほぼ終わっていました。Quake III と同じ1999年、Intel は SSE に逆平方根命令 rsqrtss を追加します。ハードウェアが直接近似を返すため、ある Core 2 の計測では rsqrtss が約0.85ns 対 高速逆平方根の約3.54ns と速く、しかも誤差も小さいという結果でした。現代のx86やArmには同等命令があり、新規に Q_rsqrt を選ぶ理由はまずありません。関連する原理はグラフィックスやプログラミングも参照してください。
0x5f3759df - (i >> 1)は、IEEE754のビット列が2を底とする対数近似になる性質を使い、右シフトで対数を半分に、定数減算で符号反転+オフセット補正を行う- 続く1行は
f(y)=1/y^2 - xに対するニュートン法1ステップ(y*(1.5 - 0.5*x*y*y))で、誤差を約3%から約0.175%へ収束させる - 定数は Lomont の
0x5f375a86がわずかに優秀だが実用差はほぼない。type punning は現代では未定義動作 - 作者は Carmack ではなく Greg Walsh(Kahan/Ng の手法が源流)。SSE
rsqrtssの登場で実務的には過去のもの - 普遍の教訓は「表現形式が演算の難易度を決める」ことと「粗い初期値×少数回の反復」で精度を稼ぐ二段構え
コード遺産の記事ガイド
Quake IIIの高速逆平方根(0x5f3759df)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
グラフィックス
比較で見る軸
難易度: advanced / カテゴリ: コード遺産 / タグ数: 6
導入後に効く点
初期値は相対誤差3%強だが、ニュートン法を1回回すと約0.175%まで収束する。定数は Chris Lomont の解析で 0x5f375a86 がわずかに優秀と判明したが、実用差はほぼない。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- コード遺産
- タグ数
- 6
判断チェックリスト
- 自社の用途が「グラフィックス / 浮動小数点」に近いか確認する。
- 強みである「floatのビット列を整数と見なし、0x5f3759df から右1ビットシフトを引くだけで 1/√x の初期値を得る。IEEE754の指数部が対数近似として働くため、この整数減算が『対数を半分にして符号反転』に相当する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。