Javascript'te Hızlı Ters Kare Kök Uygulansın mı?


11

Quake III'teki Hızlı Ters Kare Kök, kayan nokta hile kullanıyor gibi görünüyor. Anladığım kadarıyla, kayan nokta gösteriminin bazı farklı uygulamaları olabilir.

Javascript'te Hızlı Ters Kare Kökü uygulamak mümkün mü?

Aynı sonucu döndürür mü?

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;

}

Bu sorunun StackOverflow'da daha iyi sorulup sorulmayacağını bana bildirin. Oyun geliştirici kökleri ve çoğunlukla oyun geliştirici uygulamaları olduğu için burada daha uygun görünüyordu.
Atav32

4
Javascript'te işaretçiler var mı?
Pubby

2
Tüm programınızı hızlandıran "özel" bir işlevi kullanmaya cazip gelse de, hataları tanıtmanız veya sadece işleri hızlandırmamanızdır (örneğin aşağıdaki Kevin Reid'in cevabına bakınız). PremiumOptimization
Daniel Carlsson

Üzgünüm, ama Javascript ile düşük seviyeli FP optimizasyonları kullanarak ince kalmak için patates ve diyet kola ile 4 yağ hamburger sipariş gibi görünüyor . Bunu yapma, anlamsız ve saçma.
Nevermind

Hızlı ters sqrt oyun programlamada çok yaygın bir işlemdir ve tüm oyun konsolları bunu donanımda uygular. ES6 kesinlikle Math.fastinvsqrt (x) 'i dile eklemeyi düşünmelidir.
John Henckel

Yanıtlar:


15

Hile , kayan nokta sayısının bitlerini tamsayı olarak yeniden yorumlamaya ve tekrar yazılır, bu da JavaScript'te Yazılan Diziler özelliğini kullanarak mümkün olan birden fazla sayısal görünüme sahip bir ham bayt tamponu oluşturmak için mümkündür.

İşte verdiğiniz kodun gerçek bir dönüşümü; JavaScript'teki tüm aritmetik işlemler 32 bit değil, 64 bit kayan nokta olduğundan, tam olarak aynı olmadığını unutmayın, bu nedenle giriş mutlaka dönüştürülecektir. Ayrıca, orijinal kod gibi, bu, işlemci mimarisi farklı bir bayt sırası kullanıyorsa saçma sonuçlar vereceği için platforma bağlıdır ; böyle bir şey yapmanız gerekiyorsa, uygulamanızın önce tamsayıların ve kayan noktaların beklediğiniz bayt gösterimlerine sahip olduğunu belirlemek için bir test durumu yürütmesini öneririz.

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

Bir grafiğe göz atarak bunun makul sayısal sonuçlar verdiğini onayladım. Bununla birlikte, daha yüksek düzeyli JavaScript işlemleri yaptığımızdan, bunun performansı artıracağı açık değildir. Kullantığım tarayıcılarda karşılaştırmalar yaptım ve Nisan 2018 itibariyle macOS'ta Chrome, Firefox ve Safari Q_rsqrt(number)tarafından alınan sürenin% 50 ila% 80'ini aldığını gördüm 1/sqrt(number). İşte tam test kurulumum:

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 integerGerçekten mi? Yıllar önceydi, tam olarak hangi işlemleri kullandığımı hatırlamıyorum, ancak bir kerede JavaScript'te bir dizi bayt dizisini N-bit (N başlıkta tanımlandı) tamsayılarına dönüştürecek bir veri ayrıştırıcısı yazdım. Bu oldukça benzer.
2014'te
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.