Why bitwise shift with 0 in JavaScript yields weir

2019-06-28 04:27发布

问题:

Just played around with unusual bitwise operations in JavaScript and I got some weird results in some cases:

Usual cases

1 << 0            // returns 1, makes sense
100 << 0          // returns 100, makes sense
100 >> 0          // returns 100, definitely makes sense

But these, when shift by 0 bits, all yield zero

9E99 << 0         // returns 0 ..... Why all bits are cleared?
9E99 >> 0         // returns 0 also  ..... All bits cleared?
Infinity >> 0     // returns 0
Infinity << 0     // returns 0
-Infinity << 0    // returns 0 .... Can't explain why
-0 << 0           // also yields 0 not -0 itself
-0 >> 0           // also resolved to 0

What if Infinity and bitwise shift

1 << Infinity     // returns 1  .. no changes
1024 << Infinity  // returns 1024 .. no changes
1024 >> Infinity  // returns 1024 .. no changes either
Infinity >> Infinity      // 0
Infinity << Infinity      // 0

Those cases above don't make much sense to me. When shift an integer by zero bits, the value doesn't change. But when you shift Infinity by 0 bits, it actually returns you 0. Why?

I thought shifting any numeric value by 0 bit should not change its value, doesn't it?

Further, when shift a small integer value by infinity bits, the value doesn't change at all. But when you shift Infinity by any values, it is replaced by 0 instead.

I'm really curious why these phenomena happen? Is there any specifications or theory that explain these odd behaviors?

回答1:

From the MDN:

The operands of all bitwise operators are converted to signed 32-bit integers in two's complement format.

All numbers in JavaScript are IEEE754 double precision floating point numbers. They're converted to 32 bits integers before being applied bitwise operators.

More precisely, this process is described in ToInt32 in the spec:

As you see, the transformation of Infinities and -0 is precisely described.

This is a binary truncation, which also explains why 9E99 is changed into 0: This is most obvious if you look at the 32 bits at the right of (9E99).toString(2) (paste it in your console, they're all 0).



回答2:

Bit shifts and Logical operations in JavaScript are evaluated as 32-bit integers. Presence of << operator requires conversion of any object to int or 0 if the conversion fails. Infinity or 99e9 are valid ieee doubles, but fail to convert to int: 9e99 mod 2^32 == 0. A counter example 9e9|0 == 410065408 illustrates the usage of modular algebra.

This works both ways: inf << inf is equivalent to 0 << 0.



回答3:

It is according to ECMA-262.

Basically, JS compiler converts floating point arguments to 32 bit integer using the algorithm described in item 9.5 of ECMA-262. In most of the cases you mentioned the result of the conversion is 0.