将JavaScript数字转换为UInt32的黑客方法

10

编辑:由于Polyfill示例已更新,因此本问题已过时。我将保留这个问题以供参考。阅读正确答案中关于位移运算符的有用信息。


问题:

在 Mozilla Array.prototype.indexOf 页面的 Polyfill 示例中,第 7 行他们有这样的注释:

var length = this.length >>> 0; // Hack to convert object.length to a UInt32

但是Mozilla上的位移运算符规范清楚地说明,该运算符返回与左操作数相同类型的值:

位移运算符将其操作数转换为32位整数,并返回与左操作数相同类型的结果。

那么长度(length)不应该接收标准的64位浮点值吗?或者有人能指出这个hack从哪里开始吗?
3个回答

14
ECMAScript规范规定,在第5步和第8步将值转换为UInt32:http://www.ecma-international.org/ecma-262/5.1/#sec-11.7
11.7.3 无符号右移运算符(>>>)对左操作数执行由右操作数指定的零填充位的按位右移操作。
产生式ShiftExpression : ShiftExpression >>> AdditiveExpression的计算方式如下:
1. 让lref成为ShiftExpression的评估结果。 2. 让lval成为GetValue(lref)。 3. 让rref成为AdditiveExpression的评估结果。 4. 让rval成为GetValue(rref)。 5. 让lnum成为ToUint32(lval)。 6. 让rnum成为ToUint32(rval)。 7. 让shiftCount成为屏蔽rnum的所有位,但最低有效5位的结果,即计算rnum & 0x1F。 8. 返回通过lnum向右移动shiftCount位进行零填充右移的结果。空出的位用零填充。结果是一个无符号32位整数

感谢您的查找和参考规范。这也意味着我在帖子中链接的Mozilla参考指南关于位运算符是错误的。 - dot slash hack
6
将变量 num 转换为无符号 32 位整数,代码为 var uint32 = num >>> 0 - jpillora

2

实际上,结果被转换回一个数字,即64位精度的浮点数。然而,在它被转换回之前,两个操作数都被转换为 UInt32,然后执行右移操作。这在ECMAScript中有规定: http://www.ecma-international.org/ecma-262/5.1/#sec-11.7.3

length >>> 0 的净结果是将 length 转换为 UInt32,然后再转换回double类型。它有什么用处?它强制损失精度,并有效地强制该值成为1)整数,2)在[0,2^32-1]范围内。例如,如果值为-1,则会变为2 ^ 32-1 == 4294967295。如果值为3.6,则会变为3。


这正是我所想的。我认为他们确实将其用作将精度降低到整数的快捷方式。但我仍然不明白为什么他们会包含这样一个黑客。暂时我假设还有更多的东西(Mozilla 的聪明人)。 - dot slash hack
也许与Unicode字符有关?这可能是一个检查,以确保数字成为有效的Unicode点。 - Jeremy

1
如果运行这个测试,Math.floor也会执行相同的操作。如果你想在一个月之内理解自己的代码,应该避免使用这些hack方法。
var a=3.6, b = a >>> 0;
console.log(b);
console.log(Math.floor(a));

1
如果您查看Math.floor的规范(http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.9)并将其与位移运算符进行比较,您会发现Math.floor不涉及将数字值的内部表示从64位浮点数更改为有符号或无符号32位整数表示。如果您正在寻找超出小数点的精度损失,则您误读了问题。 - dot slash hack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接