トリックは、浮動小数点数のビットを整数として再解釈し、それを元に戻すことに依存しています。これは、JavaScriptで型付き配列機能を使用して、複数の数値ビューを持つ生のバイトバッファーを作成することで可能です。
ここにあなたが与えたコードの文字通りの変換があります。JavaScriptのすべての算術演算は32ビットではなく64ビットの浮動小数点であるため、完全に同じではないことに注意してください。したがって、入力は必ず変換されます。また、元のコードと同様に、これはプラットフォームに依存しているため、プロセッサアーキテクチャが異なるバイトオーダーを使用すると、意味のない結果が得られます。このようなことを行う必要がある場合は、アプリケーションで最初にテストケースを実行して、整数と浮動小数点数が期待どおりのバイト表現を持っていることを確認することをお勧めします。
const bytes = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT);
const floatView = new Float32Array(bytes);
const intView = new Uint32Array(bytes);
const threehalfs = 1.5;
function Q_rsqrt(number) {
const x2 = number * 0.5;
floatView[0] = number;
intView[0] = 0x5f3759df - ( intView[0] >> 1 );
let y = floatView[0];
y = y * ( threehalfs - ( x2 * y * y ) );
return y;
}
これが妥当な数値結果を与えることをグラフを目視することで確認しました。ただし、より高度なJavaScript操作を実行しているため、これによってパフォーマンスが向上するかどうかは明らかではありません。私が手元にあるブラウザーでベンチマークを実行したところ、(2018年4月の時点で、macOS上のChrome、Firefox、Safari)のQ_rsqrt(number)
50%から80%の時間がかかることがわかりました1/sqrt(number)
。これが私の完全なテスト設定です:
const {sqrt, min, max} = Math;
const bytes = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT);
const floatView = new Float32Array(bytes);
const intView = new Uint32Array(bytes);
const threehalfs = 1.5;
function Q_rsqrt(number) {
const x2 = number * 0.5;
floatView[0] = number;
intView[0] = 0x5f3759df - ( intView[0] >> 1 );
let y = floatView[0];
y = y * ( threehalfs - ( x2 * y * y ) );
return y;
}
// benchmark
const junk = new Float32Array(1);
function time(f) {
const t0 = Date.now();
f();
const t1 = Date.now();
return t1 - t0;
}
const timenat = time(() => {
for (let i = 0; i < 5000000; i++) junk[0] = 1/sqrt(i)
});
const timeq = time(() => {
for (let i = 0; i < 5000000; i++) junk[0] = Q_rsqrt(i);
});
document.getElementById("info").textContent =
"Native square root: " + timenat + " ms\n" +
"Q_rsqrt: " + timeq + " ms\n" +
"Ratio Q/N: " + timeq/timenat;
// plot results
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function plot(f) {
ctx.beginPath();
const mid = canvas.height / 2;
for (let i = 0; i < canvas.width; i++) {
const x_f = i / canvas.width * 10;
const y_f = f(x_f);
const y_px = min(canvas.height - 1, max(0, mid - y_f * mid / 5));
ctx[i == 0 ? "moveTo" : "lineTo"](i, y_px);
}
ctx.stroke();
ctx.closePath();
}
ctx.strokeStyle = "black";
plot(x => 1/sqrt(x));
ctx.strokeStyle = "yellow";
plot(x => Q_rsqrt(x));
<pre id="info"></pre>
<canvas width="300" height="300" id="canvas"
style="border: 1px solid black;"></canvas>