【问题标题】:Fast nearest power of 2 in JavaScript?JavaScript中最接近2的幂?
【发布时间】:2015-01-13 22:06:20
【问题描述】:

下面的表达式是否有更快的替代方法:

Math.pow(2,Math.floor(Math.log(x)/Math.log(2)))

也就是说,取最接近(较小)的 2 的整数幂?我在内循环中有这样的表达。我怀疑它可能会快得多,考虑到可以从双精度的 IEEE 754 表示中获取尾数。

【问题讨论】:

  • 你为什么不硬编码 log 2 的值?还是那太可变了?
  • 啊,我当然可以。但是我仍然在取一个对数,然后除法,然后取一个楼层,然后取 2 的幂。这已经太多了,当信息都已经在 double 本身上时!如果我能投...
  • 我实际上已经阅读了整个线程......它的大部分答案都处理整数并且主要依赖于 JS 上不可用的 C hacks......
  • 该表达式并不总是适用于接近 2 次方的双打。例如,尝试 x=35184372088831.9(略低于 2^45)。您的表达式返回 2^45,而不是 2^44。

标签: javascript double bit-manipulation ieee-754


【解决方案1】:

这是另一种选择,带有基准。虽然两者似乎都具有可比性,但我喜欢能够落地或天花板。

function pow2floor(v) {
  var p = 1;
  while (v >>= 1) {
    p <<= 1;
  }
  return p;
}

function pow2ceil(v) {
  var p = 2;
  while (v >>= 1) {
    p <<= 1;
  }
  return p;
}

function MATHpow2(v) {
  return Math.pow(2, Math.floor(Math.log(v) / Math.log(2)))
}


function nearestPowerOf2(n) {
   return 1 << 31 - Math.clz32(n);
}

function runner(fn, val) {
  var res;
  for (var i = 0; i < 10000000; i++) {
    fn(val);
  }
  return
}

var then;
var source = 3000;

then = new Date().getTime();
var a = runner(pow2floor, source);
console.log("\n--- pow2floor ---");
console.log(" result: " + pow2floor(source));
console.log(" time: " + (new Date().getTime() - then) + " ms");

then = new Date().getTime();
var b = runner(MATHpow2, source);
console.log("\n--- MATHpow2 ---");
console.log(" result: " + MATHpow2(source));
console.log(" time: " + (new Date().getTime() - then) + " ms");

then = new Date().getTime();
var b = runner(nearestPowerOf2, source);
console.log("\n--- nearestPowerOf2 ---");
console.log(" result: " + nearestPowerOf2(source));
console.log(" time: " + (new Date().getTime() - then) + " ms");

// my results (results vary by system and browser)
//  pow2floor:       130 ms loser
//  MATHpow2:        147 ms loser
//  nearestPowerOf2: 47 ms clear winner!

【讨论】:

    【解决方案2】:

    利用 ES6 的 Math.clz32(n) 计算 32 位整数的前导零:

    // Compute nearest lower power of 2 for n in [1, 2**31-1]:
    function nearestPowerOf2(n) {
      return 1 << 31 - Math.clz32(n);
    }
    
    // Examples:
    console.log(nearestPowerOf2(9));  // 8
    console.log(nearestPowerOf2(33)); // 32

    【讨论】:

    • 理论上应该很快,但我还没测试。
    • 但要注意 MDN 的 polyfill。它说 clz32(8) 应该是 28 时是 29,所以如果在这个最近的PowerOf2 实现中使用,你会得到最近的PowerOf2(8) = 4。
    【解决方案3】:

    不幸的是,您需要 C 函数 frexp 的等效项。我能找到的最好的是JSFiddle,它的代码使用Math.pow

    您可以使用真实数据以及您当前的尝试对几个替代方案进行基准测试:

    1. 从 1.0 开始,反复乘以 2.0 直到大于或等于输入,然后乘以 0.5 直到小于或等于输入。您需要对双精度范围末端的值进行特殊处理。
    2. 在 double 范围内存储 2 的所有精确幂的升序值数组,然后进行二分搜索。

    如果您的数据通常接近 1.0,则第一个可能最快。第二个需要最多 11 个条件分支。

    【讨论】:

      猜你喜欢
      • 2011-07-11
      • 2011-05-22
      • 2023-03-28
      • 1970-01-01
      • 2016-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多