在Java中实现快速逆平方根?


11

Quake III的Fast Inverse Square Root似乎使用了浮点技巧。据我了解,浮点表示可以有一些不同的实现。

那么有可能在Javascript中实现快速逆平方根吗?

它会返回相同的结果吗?

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 ) );

  return y;

}

让我知道是否最好在StackOverflow上问这个问题。这似乎更合适,因为它具有游戏开发者的根源,并且主要是游戏开发者的应用程序。
Atav32 2012年

4
JavaScript有指针吗?
Pubby 2012年

2
尽管它试图使用“特殊”功能来加快整个程序的速度,但很可能是您引入了错误或根本就没有加快任何速度(例如,请参见下面的凯文·里德的回答)。c2.com/cgi/wiki?PrematureOptimization
Daniel Carlsson

抱歉,将低级FP优化与Javascript结合使用看起来像是订购4个肥肉汉堡,薯条和减肥可乐,以保持瘦身。不要那样做,这是毫无意义和荒谬的。
没关系

快速反向sqrt在游戏编程中是非常常见的操作,并且所有游戏机都在硬件中实现此操作。ES6绝对应该考虑将Math.fastinvsqrt(x)添加到语言中。
约翰·亨克尔

Answers:


15

诀窍在于将浮点数的位重新解释为整数,然后再返回,这在JavaScript中可以通过使用Typed Arrays工具创建,以创建具有多个数字视图的原始字节缓冲区。

这是您提供的代码的文字转换;请注意,这并不完全相同,因为JavaScript中的所有算术运算都是64位浮点而不是32位,因此必须转换输入。同样,与原始代码一样,这与平台有关,如果处理器体系结构使用不同的字节顺序,它将给出无意义的结果。如果必须执行此类操作,则建议您的应用程序首先执行一个测试用例,以确定整数和浮点数是否具有所需的字节表示形式。

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操作,因此这根本不会提高性能是显而易见的。我已经在方便的浏览器上运行了基准测试,发现这Q_rsqrt(number)花费了50%到80%的时间1/sqrt(number)(截至2018年4月,macOS上的Chrome,Firefox和Safari)。这是我完整的测试设置:

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>


In classic JavaScript, it is not possible to... reinterpreting the bits of a floating-point number as an integer真?那是几年前的事,所以我不记得确切在使用什么操作,但是我曾经用JavaScript编写过一个数据解析器,它将一串字节转换为一系列N位(在标头中定义N)整数。那很相似。
跳动
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.