✦ スクエアノーマライズ(w² NDC)
― float 時代の丸め誤差の常識から導く NDC 安全化 ―
■ はじめに
3D グラフィックスでは、0〜1 の正規化空間(NDC) が
深度バッファやクリップ空間の基盤として使われています。
しかしこの 0–1 境界は非線形バグの温床であり、
深度の near 付近で破綻したり、丸め誤差が集中したりと、
多くのエンジンが暗黙のうちに苦しんでいる領域でもあります。
私は自作 3D エンジンで 無限対数軸を用いた無限遠透視射影 を設計する過程で、
この境界問題を避けるために Range² 正規化 を採用しました。
そしてこの知見は、NDC にも一般化できます。
それが スクエアノーマライズ(w² NDC) です。
■ 浮動小数点(特に float 時代)の常識
スクエアノーマライズを理解するには、
まず float 時代の丸め誤差の常識 を押さえる必要があります。
● 同値でも計算経路が違えば結果が変わる
次の式は数学的には完全に同値です。
$$
\frac{10}{3} = \frac{30}{9}
$$
しかし float では挙動が変わる可能性があります。
-
10/3
- 小さい値同士の割り算
- 有効桁が少ない
- 桁落ちしやすい
-
30/9
- 大きい値同士の割り算
- 有効桁が多い
- 桁落ちしにくい
つまり:
数学的に同値でも、途中値のスケールが違うと丸め誤差の出方が変わる。
float 時代の常識そのものです。
● x/w は「巨大分母」に弱い(アンダーフロー → ゼロ除算)
$$
\frac{x}{w}
$$
は w が極端に巨大な場合にアンダーフローしやすい。
- x がゼロに丸められる
- その後の計算で ゼロ除算 が発生する
これは float 時代の「典型的なレアケースのバグ」です。
特に 3D グラフィックスでは、
w が巨大になるケース(遠方オブジェクト) が普通に存在するため、
この問題は無視できません。
● wx/w² は巨大分母によるゼロ丸めを回避しやすい
$$
\frac{wx}{w^2}
$$
数学的には x/w と完全に同値ですが、
浮動小数点では途中値のスケールが違うため挙動が変わります。
● wx/w² の利点
- 分子 wx が w スケール空間に持ち上がる
- x がゼロに丸められにくくなる
- ゼロ除算のレアケースを回避できる可能性がある
つまり:
巨大分母によるゼロ丸め → ゼロ除算
このレアケースを救うための安全化がスクエアノーマライズ。
● さらに複雑な例
$$
\frac{10}{333} \times \frac{50}{3}
= \frac{10 \times 50}{333 \times 3}
$$
数学的には同じ値ですが、float では:
- ゼロ除算を回避できるケースもある
- ∞ に振れるケースもある
- 逆のケースもありうる
つまり:
途中値のスケールが違うだけで、
浮動小数点の挙動は大きく変わる。
■ 無限対数軸と Range² 正規化
無限対数軸では、次の式が登場します。
$$
V'.x = \frac{(Range - \text{InfLogAxis}(z)) \cdot V.x}{Range^2}
$$
ここで:
- $((Range - \text{InfLogAxis}(z)))$ は 0〜Range
- $(V.x)$ も 0〜Range
- 分子は 0〜Range²
- Range² で割ると 0〜1 に正規化される
つまり:
安全スケール空間(0〜Range) → Range² 正規化 → 0〜1
という二段階構造です。
■ NDC の w は Range に相当する
通常の NDC はこうです。
$$
x_{ndc} = \frac{x_c}{w_c}
$$
ここで $w_c$ は距離スケール(=Range) として機能しています。
- w が大きい → 遠い → 安定
- w が小さい → near → 危険
つまり:
NDC の w は無限対数軸の Range と同じ役割を持つ。
■ スクエアノーマライズ(w² NDC)
無限対数軸の Range² 正規化を NDC に一般化すると:
$$
x_{ndc}^{(w^2)} = \frac{w_c x_c}{w_c^2}
$$
これは数学的には $x_c / w_c$ と同値ですが、
float では途中値のスケールが違うため挙動が変わります。
つまり:
wx / w² は、x / w を浮動小数点の観点で安全化した形。
これを スクエアノーマライズ と呼びます。
■ スクエアノーマライズのメリット
● 巨大分母によるゼロ丸め → ゼロ除算のレアケースを回避
これが最大の目的。
● 0–1 境界に直接突っ込まない
丸め誤差が near に集中しない。
● 深度バッファの near 破綻を緩和
深度精度が改善される。
● 無限対数軸と NDC の設計思想が統一される
投影の一貫性が増す。
● ただしこれらは期待される方向性だけであり逆作用もあるし効果がないことが正常
■ 計算経路の違いによるゼロ除算および数値破綻の抑制
数学的な恒等変形($wx / w^2 = x / w$)であっても、浮動小数点演算(IEEE 754)においては、計算経路の順序と途中値のスケールが変わることで、ハードウェアレベルの丸め処理や例外発生の挙動が根本から異なります。特に $w$ がゼロ近傍や表現限界の極限値に近づいた際、この経路の違いが致命的なバグを防ぐ防波堤として機能します。
-
直接的な割り算($x / w$)の脆弱性
分母に極小の $w$ が入った瞬間、単精度の仮数部表現やハードウェアの除算器において値が即座に爆発し、無限大(Infinity)やNaN(非数)を生成します。これがラスタライザやクリッピングステージに流れ込むことで、ピクセル単位の不正終了やオブジェクトの突然の消失(神隠し)を引き起こす可能性があります。 -
スクエアノーマライズ的経路($wx / w^2$)によるクッション効果
分子と分母の両方にあらかじめ $w$(あるいは $w^2$)を噛ませることで、割り算という最も情報落ちや例外が起きやすいクリティカルな演算を行う直前の「数値のスケールと密度」が変化します。 -
「効果がないことが正常」である理由
通常の描画範囲では数学的同値性により結果は一致しますが、破綻寸前の極限状態においてのみ、この途中値のスケール操作が丸め誤差のベクトルを安全な方向(アンダーフローによる穏やかなゼロ丸めなど)へ誘導し、システム全体がNaNの海に沈むのを未然に防ぐことを期待します。また実際それで改善されるケースがあります。
■ 具体数値例
例えば浮動小数点保証精度が1e-6だとして
x=3,w=1e7,x/w=3e-7→0
ですが
wx=3e7,w^2=1e14,wx/w^2=3e-7→0
なことはそれもそうなのですが
計算経路が違うために、瞬間的なゼロへの丸めの発動が抑制される場合があります。
スクエアノーマライズは数学的同値だが、
浮動小数点では途中値のスケールが変わることで
極限状態の NaN・Inf・ゼロ除算の発生確率をわずかに減らす
“安全弁としての数値経路変更”である。
スクエアノーマライズは、数学的同値性を保ちながら
浮動小数点演算の途中値スケールを変えることで
瞬間的なゼロ丸め・∞発生・NaN の発生を抑制してその後の計算破綻を防ぐことを期待。
通常は完全に同値で効果ゼロだが、
極限状態だけ例外発生を防ぐことが期待される。
これは CPU が 100%稼働で熱暴走するため、
最大稼働率を 80〜95%に抑えて OS の緊急保護発動を防ぐ
という対策と同じ構造である。
具体ケースが多岐にわたり一般理論化が難しく
実験的・経験的にしか語れず反証可能性も低いので
オカルトチックと思うならばオカルト/信仰の類の理屈かもね
■ 計算経路を変えるという考え方
この考え方は、次のような式にも現れます。
$$
\frac{10}{333} \times \frac{50}{3} =
\frac{10\times50}{333\times3}
$$
数学的にはもちろん同じ値です。
しかし浮動小数点演算では、
$$
\left(\frac{10}{333}\right)\times\left(\frac{50}{3}\right)
$$
と、
$$
\frac{10\times50}{333\times3}
$$
では、途中で行われる演算の順序と中間値が異なります。
そのため、最終的な数学的結果が同じであっても、浮動小数点演算では丸め誤差によって微妙に異なる結果になる場合があります。一番問題になるのが意図しない瞬間的なゼロへの丸めと∞の発生です。
■ 核心的な具体例
例えば浮動小数点保証精度が1e-6の場合
$$
\frac{10}{605}\times\frac{999}{7e12}\times\frac{4e9}{215}
=\frac{10\times999\times4e9}{605\times7e12\times215}
$$
左辺が瞬間的にゼロになってその後破綻する可能性と
右辺が安全な可能性があるのですが
特に具体的には
$\frac{999}{7e12}$でのゼロの発生と回避です
さらに、このような計算経路の変更によって、
- アンダーフローを回避できる場合
- ゼロ除算を回避できる場合
- オーバーフローを回避できる場合
- 逆にオーバーフローを発生させる場合
- 逆にアンダーフローを発生させる場合
- 丸め誤差が大きくなる場合
など、状況によって結果が変わる可能性があります。
これは「数学的に別の値を求める」ものではなく、同じ値を、浮動小数点数にとって異なる計算経路で求めるという発想です。
■ まとめ
スクエアノーマライズ(w² NDC)は、
- float 時代の丸め誤差の常識
- 巨大分母によるゼロ丸め → ゼロ除算のレアケース
- 無限対数軸の Range² 正規化
- NDC の w が Range に相当するという構造
- 途中値のスケールを利用した誤差分散
- しかし効果がないことが正常なので、レアケース防御策としてメリットもデメリットも両面があるものです
- またGPU/シェーダー周りの実装では未だfloatが主流なのでまれにこういう理由でオブジェクトが消失したりすることがある
これらを統合した合理的な設計です。
数学的には同値でも、浮動小数点では途中値のスケールが違うため
挙動が変わる。
その性質を NDC の安全化に応用したのがスクエアノーマライズである。
作者
GitHub: https://github.com/NAS6mixfoolv
X(旧Twitter): https://x.com/NAS6_oxo
作者HP: https://nas6.net
気に入っていただけたら GitHub に ⭐ をいただけると嬉しいです!