Chrome中的Math.log2精度发生了变化
Math.log2 precision has changed in Chrome
我编写了一个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
的精度发生了变化吗?有没有比我上面添加epsilon更优雅的修改?
注意:
Math.log2
自实施以来实际上没有发生任何更改在V8中。也许你记错了,或者你包含了一个垫片碰巧在Chrome之前得到了这些特殊情况的正确结果包括它自己的CCD_ 3的实现。此外,您似乎应该使用
Math.ceil(x)
,而不是Math.floor(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
Math.log2
目前是如何在不同的浏览器中实现的
Chrome中的当前实现是不准确的,因为它们依赖于Math.log(x)
的值乘以Math.LOG2E
,这使得它容易受到舍入误差的影响(来源):
// ES6 draft 09-27-13, section 20.2.2.22.
function MathLog2(x) {
return MathLog(x) * 1.442695040888963407; // log2(x) = log(x)/log(2).
}
如果您运行的是Firefox,它要么使用本机log2
功能(如果存在),要么使用与Chrome类似的实现(源代码)。
唯一的区别是,它们不是相乘,而是除以log(2)
:
#if !HAVE_LOG2
double log2(double x)
{
return log(x) / M_LN2;
}
#endif
乘法或除法:有多大区别
为了测试除以Math.LN2
和乘以Math.LOG2E
之间的差异,我们可以使用以下测试:
function log2d(x) { return Math.log(x) / Math.LN2; }
function log2m(x) { return Math.log(x) * Math.LOG2E; }
// 2^1024 rounds to Infinity
for (var i = 0; i < 1024; ++i) {
var resultD = log2d(Math.pow(2, i));
var resultM = log2m(Math.pow(2, i));
if (resultD !== i) console.log('log2d: expected ' + i + ', actual ' + resultD);
if (resultM !== i) console.log('log2m: expected ' + i + ', actual ' + resultM);
}
请注意,无论使用哪种函数,对于某些值1,它们仍然有浮点错误。恰好log(2)
的浮点表示小于实际值,导致值高于实际值(而log2(e)
较低)。这意味着对于这些特殊情况,使用log(2)
将向下取整到正确的值。
1:log(pow(2, 29)) / log(2) === 29.000000000000004
您也许可以这样做,而不是
// Math.log2(n_elements) to 10 decimal places
var tree_depth = Math.floor(Math.round(Math.log2(n_elements) * 10000000000) / 10000000000);
- 起重行为在铬 48 和 49 之间发生了变化
- 找出 Vue.js 深度自定义指令中哪个属性发生了变化
- 发送到前端后,mysql 列中的日期发生了变化
- 检索到的 $().position() 更新为 position: absolute 后发生了变化
- 为什么 Angular 没有注意到这个变量的值发生了变化
- 自定义图标行为是否在JQueryMobile 1.4(Alpha / Beta / RC-1)中发生了变化
- Angular ngClass 没有更新我的类,即使我的条件按预期发生了变化
- 是这里的范围发生了变化还是其他原因
- 主页链接'的背景颜色在Internet explorer中发生了变化,但在Mozilla Firefox中不起作
- Chrome中的Math.log2精度发生了变化
- JS SDK auth.logout事件行为最近似乎发生了变化
- 为什么我的变量“c”发生了变化
- 使用JavaScript,这样当点击图像时,另一个元素's的位置发生了变化
- ag网格对单元格值的排序发生了变化
- 当我插入MySQL时,我的代码发生了变化
- 隐藏提交按钮,除非表单/输入数据发生了变化
- react.js如何知道数据发生了变化?
- 找出在Ember ArrayController中哪个单独的项目发生了变化
- 将html移到php include后,情况发生了变化
- 如何检测表单输入的值自设置以来是否发生了变化