RedScript v2.5.0:倍精度演算・N次ベジェ曲線・標準ライブラリ刷新
RedScript v2.5.0:倍精度演算・N次ベジェ曲線・標準ライブラリ刷新
RedScript開発における一つのマイルストーン:v2.5.0がリリースされた。完全に再設計された数値型システム、Minecraftのスコアボードエンジン上で動作するIEEE 754倍精度浮動小数点演算、N次ベジェ曲線、そしてテストケース数を1277から1485へと大幅に拡張した標準ライブラリが含まれる。今回のリリースの詳細をお伝えする。
型システムの再設計
最初の変更は、ずっと必要だった命名の整理だ。旧 float 型——実際には浮動小数点ではなかった——は fixed(固定小数点)に改名された。値はスコアボード整数として×10000のスケールで格納される、32ビット固定小数点型だ。旧名称は明らかに誤解を招くものだった。
これに加え、新しい double 型が言語に追加された:本物のIEEE 754 64ビット浮動小数点数で、スコアボードではなく rs:d 名前空間のNBTストレージに格納される。これは本質的に別物だ——スコアボードではなくNBTに存在し、操作にはスコアボード演算ではなく専用の関数呼び出しが必要となる。
両者の型変換は明示的のみとなった。変換が発生する箇所では x as double または x as fixed の記述が必須となる。コンパイラは数値型を暗黙的に強制変換しなくなった。新しいlint診断 [FloatArithmetic] が追加され、旧 float 名が算術式に現れるたびに警告を発する——既存コードベースの移行を支援するためのツールだ。
Double演算:Minecraftの黒魔術
この部分が最もクリエイティブなエンジニアリングを要した。Minecraftのスコアボードシステムは32ビット整数で動作し、ネイティブな浮動小数点命令は一切持たない。しかしエンティティ座標と double 型のNBT格納値は、JVM内部でIEEE 754 64ビット倍精度浮動小数点数として保存されている。その事実を間接的に利用することがポイントだ。
加算:エンティティ座標トリック
Minecraftエンティティは Pos 配列を倍精度浮動小数点数として保存する。鍵となる洞察は、loot spawn が任意の座標にエンティティを配置でき、範囲制限がないという点だ(tp コマンドのように±30,000,000にクランプされない)。マーカーエンティティを生成し、相対テレポートを使って加算を行う:
# a + b:
$tp <marker> $(x) 0 0
# → markerをx = aにテレポート(関数マクロ経由で注入)
$execute at <marker> run tp <marker> ~$(dx) 0 0
# → markerを+bだけ移動(相対移動なので最終的にx = a + b)
data get entity <marker> Pos[0]
# → 結果を読み取り:a + bがdoubleとして取得される相対 tp 命令(~)がJVMレベルで倍精度の加算を実行する。ゲーム自身の座標計算を浮動小数点ALUとして利用しているわけだ。
乗算:関数マクロによるスケール注入
execute store の scale 引数はコンパイル時リテラルでなければならない——変数を渡すことはできない。回避策として、MCの関数マクロを使ってスケールを実行時に動的に注入する:
execute store result storage rs:d __scale double 0.0001 \
run scoreboard players get $f __ns
# → __scale = f × 0.0001(f/10000を保持するdouble)
$execute store result storage rs:d out double $(scale) \
run data get storage rs:d input 1
# → out = input × $(scale) = d × (f/10000)__scale を事前にdoubleとして計算し、マクロを使って execute store コマンドに埋め込むことで、コンパイル時の定数畳み込みなしにfixed × doubleの乗算を実現している。
除算:Display Entity SVD
MinecraftのDisplay EntityはSVD(特異値分解)でゲームが分解する変換行列を持ち、平行移動・回転・スケールを抽出する。スケール成分が input / divisor となるように行列を構築することで、ゲームエンジンがハードウェア上で除算を実行してくれる——実質的に浮動小数点の除算をレンダラーの行列分解コードにオフロードしているわけだ。
コンパイラ組み込み関数
これらの仕組みはすべて言語レベルでは透過的だ。a と b がともに double 変数である場合、a + b と書くとMIRの降格時に自動的に double_add(a, b) 呼び出しへと変換される。コンパイラはオペランドの型に基づいて適切な組み込み関数を選択する。ユーザーコードへの構文変更は不要だ。
新しい標準ライブラリモジュール
parabola.mcrs
投射物シミュレーションのための弾道軌跡計算:
- 始点位置、目標変位、望ましい飛行時間から初速ベクトルを計算
- 一定重力下での時刻
tにおける位置を評価 - 設定可能な減衰係数による空気抵抗シミュレーション
quaternion.mcrs
Display Entityの回転は内部でクォータニオンを使用している。このモジュールはそれを駆動するために必要な完全なクォータニオン代数をカバーする:
- クォータニオンの乗算・共役・正規化
- なめらかな回転遷移のためのSLERP補間
- オイラー角からクォータニオンへの変換
注目すべきバグも修正された:SLERPの正規化で mulfix を呼び出す際に誤ったスケールを使用しており、大きさが期待する10000ではなく約31622になっていた。問題は正規化ステップで平方根の計算が抜けていたことで——中間値が二乗のまま使われ、スケールが√10000 = 100倍に膨らんでいた。
bezier_quartic + bezier_n
De CasteljauアルゴリズムによるN次ベジェ曲線。bezier_n は制御点配列をインプレースで操作する(破壊的だが高速)。bezier_n_safe は先にコピーを作成し、繰り返し評価に備えて元の制御点を保持する。両関数とも固定小数点パラメータ t ∈ [0, 10000] を受け取る。
bigint_mul / bigint_sq
スコアボード配列を使った任意精度整数演算。bigint_mul は2つの多肢整数を乗算する。bigint_sq は対称性を利用して乗算回数をおよそ半分に削減した最適化済みの二乗演算ルーチンだ。
高精度数学
ln_hp:ニュートン・ラフソン法による精度向上
既存の ln_5term 関数は5項級数で ln(x) を近似し、約6桁の有効数字精度を提供する。ln_hp はその上にニュートン・ラフソン法の補正ステップを1回追加したラッパーだ:
y₀ = ln_5term(x) # 初期推定値
correction = (x - exp(y₀)) × 10000 / exp(y₀)
y₁ = y₀ + correction # 精度向上後の結果補正は初期推定値の近傍の値に対して1ステップで機械精度に収束し、精度を8〜9桁の有効数字まで向上させる——ほとんどの物理シミュレーションには十分な精度だ。
signal.mcrsの統計分布
信号処理モジュールに4つの新しいサンプリング関数が追加された:
gamma_sample(k, θ)— 整数形状パラメータkに対する指数の和による方法poisson_sample(λ)— Knuthのアルゴリズム(一様乱数の積がe^−λ未満になるまで繰り返す)negative_binomial_sample(r, p)— ポアソン・ガンマ複合分布geometric_sample(p)— 逆CDF閉形式:⌊ln(U) / ln(1−p)⌋
これらによりパーティクルシステム、プロシージャル生成、確率的なAI動作をデータパック内で完結させることができる——外部ツールは一切不要だ。
数値指標
| 指標 | 更新前 | 更新後 | 変化 |
|---|---|---|---|
| テストケース数 | 1277 | 1485 | +208 |
| 標準ライブラリ関数 | — | 約40個追加 | — |
| 数値型 | float、int | fixed、double、int | +1 |
今回のリリースのすべてのコミットはGPG署名済みだ。変更履歴はリポジトリルートの CHANGELOG.md にある。
v2.5.0はRedScriptの数値モデルに対して、言語が生まれてから最も大きな変化をもたらした。倍精度演算により、これまでデータパックでは手の届かなかった物理・レンダリングの問題が解決できるようになった。標準ライブラリの拡張により、それらの基礎部品をすべてのプロジェクトで一から作り直すことなく活用できるようになった。
