Javascript Chrome中的Math.log2精度已更改
我编写了一个JavaScript程序,它根据元素的数量计算二叉树的深度。我的程序已经运行了好几个月了,但最近我发现在Chrome和Firefox中浏览网页时有了不同 特别是在Firefox上: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
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;
我有两个问题:
你或许可以这样做
// 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);
}