Hack to convert javascript number to UInt32
Asked Answered
G

3

11

Edit: This question is out of date as the Polyfill example has been updated. I'm leaving the question here just for reference. Read the correct answer for useful information on bitwise shift operators.


Question:

On line 7 in the Polyfill example of the Mozilla Array.prototype.indexOf page they comment this:

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

But the bitwise shift specification on Mozilla clearly states that the operator returns a value of the same type as the left operand:

Shift operators convert their operands to thirty-two-bit integers and return a result of the same type as the left operand.

So shouldn't length receive the standard 64-bit float value? Or can someone point out to me where the hack starts?

Gesticulate answered 11/3, 2014 at 20:24 Comment(0)
S
15

The ECMAScript specification states that the value is converted to UInt32 in step 5 and 8 of http://www.ecma-international.org/ecma-262/5.1/#sec-11.7:

11.7.3 The Unsigned Right Shift Operator ( >>> )

Performs a zero-filling bitwise right shift operation on the left operand by the amount > specified by the right operand.

The production ShiftExpression : ShiftExpression >>> AdditiveExpression is evaluated as follows:

  1. Let lref be the result of evaluating ShiftExpression.
  2. Let lval be GetValue(lref).
  3. Let rref be the result of evaluating AdditiveExpression.
  4. Let rval be GetValue(rref).
  5. Let lnum be ToUint32(lval).
  6. Let rnum be ToUint32(rval).
  7. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
  8. Return the result of performing a zero-filling right shift of lnum by shiftCount bits. Vacated bits are filled with zero. The result is an unsigned 32-bit integer.
Sarmentose answered 11/3, 2014 at 20:28 Comment(2)
Thanks for the lookup and points for referencing the spec. That also means that the Mozilla reference guide linked in my post is wrong about bitwise operators.Gesticulate
TLDR; var uint32 = num >>> 0Bradfordbradlee
H
2

The result is, indeed, converted back to a number, i.e., a 64-bit precision floating point number. However, before it is converted back, both operands are converted to UInt32, then the right shift operation is performed. This is specified in ECMAScript here: http://www.ecma-international.org/ecma-262/5.1/#sec-11.7.3

The net result of length >>> 0 is therefore, because >>> 0 itself is a no-op, to convert length to a UInt32 then back to a double. What is it good for? It forces the loss of precision, and effectively forces the value to be 1) and integer and 2) in the range [0, 2^32-1]. For example, if it was -1, it will become 2^32-1 == 4294967295. If it was, 3.6, it will become 3.

Hammerskjold answered 11/3, 2014 at 20:33 Comment(2)
That is exactly what I was thinking. I think they are indeed using it as a shortcut to drop precision to an integer. But I still don't understand why they'd include such a hack. I'm assuming there's something more for the moment (smart guys at Mozilla)Gesticulate
Maybe something related to unicode characters? That could be a check to make sure that the number becomes a valid unicode point.Lebaron
M
1

If you run this test, Math.floor will do the same. Those hacks should be avoided if you want to understand your own code in a month or so.

var a=3.6, b = a >>> 0;
console.log(b);
console.log(Math.floor(a));
Mechling answered 5/6, 2014 at 15:34 Comment(1)
If you look at the specification of Math.floor ( ecma-international.org/ecma-262/5.1/#sec-15.8.2.9 ) and compare it with Bitwise shift operator, you can see that Math.floor doesn't involve changing the internal representation of the number value from 64-bit floating point to singed or unsigned 32-bit integer representation. If it's losing precision beyond the decimal point you're going for, then you misread the question.Gesticulate

© 2022 - 2024 — McMap. All rights reserved.