Javascript Chrome中的Math.log2精度已更改

Javascript Chrome中的Math.log2精度已更改,javascript,precision,Javascript,Precision,我编写了一个JavaScript程序,它根据元素的数量计算二叉树的深度。我的程序已经运行了好几个月了,但最近我发现在Chrome和Firefox中浏览网页时有了不同 特别是在Firefox上: Math.log2(8) = 3 但现在在Chrome中: Math.log2(8) = 2.9999999999999996 我编写的JavaScript程序最初是为了根据元素数查找二叉树的深度,如下所示: var tree_depth = Math.floor(Math.log2(n_elemen

我编写了一个JavaScript程序,它根据元素的数量计算二叉树的深度。我的程序已经运行了好几个月了,但最近我发现在Chrome和Firefox中浏览网页时有了不同

特别是在Firefox上:

Math.log2(8) = 3
但现在在Chrome中:

Math.log2(8) = 2.9999999999999996
我编写的JavaScript程序最初是为了根据元素数查找二叉树的深度,如下所示:

var tree_depth = Math.floor(Math.log2(n_elements)) + 1;
我对这个公式做了一个简单的修改,这样它在Chrome上仍然可以正常工作:

var epsilon = 1.e-5;
var tree_depth = Math.floor(Math.log2(n_elements) + epsilon) + 1;
我有两个问题:

  • 最近有没有人注意到Chrome中Math.log2的精度发生了变化

  • 有没有比我在上面添加ε更优雅的修改


  • 你或许可以这样做

    // Math.log2(n_elements) to 10 decimal places
    var tree_depth = Math.floor(Math.round(Math.log2(n_elements) * 10000000000) / 10000000000);
    
    注意:
    Math.log2
    自从实现以来,实际上没有改变 在V8中。可能您记错了,或者您包含了一个垫片 在Chrome之前,这些特殊情况下的结果是正确的 包括它自己的
    Math.log2
    实现

    此外,您似乎应该使用
    Math.ceil(x)
    而不是
    数学地板(x)+1

    我怎样才能解决这个问题? 为了避免依赖于
    Math.log
    Math.log2
    在不同的JavaScript实现中是准确的(使用的算法是实现定义的),如果二叉树中的元素少于232个,则可以使用位运算符。这显然不是最快的方法(这只是O(n)),但这是一个相对简单的例子:

    function log2floor(x) {
      // match the behaviour of Math.floor(Math.log2(x)), change it if you like
      if (x === 0) return -Infinity;
    
      for (var i = 0; i < 32; ++i) {
        if (x >>> i === 1) return i;
      }
    }
    
    console.log(log2floor(36) + 1); // 6
    
    如果您运行的是Firefox,它要么使用原生的
    log2
    函数(如果存在),要么使用与Chrome()类似的实现

    唯一的区别是,它们不是相乘,而是除以
    log(2)

    #if !HAVE_LOG2
    double log2(double x)
    {
        return log(x) / M_LN2;
    }
    #endif
    
    double
    js::math_log2_impl(MathCache *cache, double x)
    {
        return cache->lookup(log2, x, MathCache::Log2);
    }
    
    double
    js::math_log2_uncached(double x)
    {
        return log2(x);
    }
    
    bool
    js::math_log2(JSContext *cx, unsigned argc, Value *vp)
    {
        return math_function<math_log2_impl>(cx, argc, vp);
    }
    
    请注意,无论您使用哪个函数,对于某些值1,它们仍然存在浮点错误。恰好
    log(2)
    的浮点表示小于实际值,导致值高于实际值(而
    log2(e)
    较低)。这意味着对于这些特殊情况,使用
    log(2)
    将四舍五入到正确的值


    1:
    log(pow(2,29))/log(2)==29.00000000000000 4

    实际上这是V8中的一个变化。是的,是的真正让我吃惊的是一个著名的垫片(
    Math.log2=function(x){return Math.log(x)/Math.LN2};)
    实际上返回了Chrome中精确幂的正确结果。谢谢-我认为shim肯定比添加epsilon更优雅。你不能用
    Math.round
    而不是
    Math.floor
    ?@soktinpk:OP寻找二叉树的深度,它不会在中间点取整。稍微相关:(如果不是很明显的话,它们都会生成整数log2)我打开了Chrome和Firefox的一个新的空白实例,转到开发者工具控制台,输入Math.log2(8)。我仍然在Firefox中得到3,在Chrome中得到2.99999999996,所以我认为我没有垫片造成差异。@Stefanmusara:如果我没有说清楚,很抱歉。有时人们在还没有函数的浏览器中添加代码来支持
    Math.log2
    ,所以他们会添加类似
    if(!Math.log2)Math.log2=function(x)的内容{return Math.log(x)/Math.LN2};
    。如果您之前在Chrome添加
    Math.log2
    函数之前测试过这个函数,那么现在的结果将与以前不同。
    #if !HAVE_LOG2
    double log2(double x)
    {
        return log(x) / M_LN2;
    }
    #endif
    
    double
    js::math_log2_impl(MathCache *cache, double x)
    {
        return cache->lookup(log2, x, MathCache::Log2);
    }
    
    double
    js::math_log2_uncached(double x)
    {
        return log2(x);
    }
    
    bool
    js::math_log2(JSContext *cx, unsigned argc, Value *vp)
    {
        return math_function<math_log2_impl>(cx, argc, vp);
    }
    
    function log2d(x) { return Math.log(x) / Math.LN2; }
    function log2m(x) { return Math.log(x) * Math.LOG2E; }
    
    var pow = Math.pow;
    
    // 2^1024 rounds to Infinity
    for (var i = 0; i < 1024; ++i) {
      var resultD = log2d(pow(2, i));
      var resultM = log2m(pow(2, i));
    
      if (resultD !== i) console.log('log2d: expected ' + i + ', actual ' + resultD);
      if (resultM !== i) console.log('log2m: expected ' + i + ', actual ' + resultM);
    }