I am implementing decoding of BER-compressed integers and recently I've found a weird JavaScript behavior related to bitwise operations with big integers.
E.g.:
var a = 17516032; // has 25 bits
alert(a << 7) // outputs -2052915200
alert(a * 128) // outputs 2242052096
alert(2242052096 >> 16) // outputs -31325
alert(2242052096 / 65536) // outputs 34211
While the first workaround (multiplication instead of left shift) is acceptable, the second isn't.
Why it happens? How to bear with it?
17516032
in binary is 00000001000010110100011000000000
. Shifting to the left by 7 gives you 10000101101000110000000000000000
. This is equal to -2052915200
in two's complement (which is how almost all computers represent negative numbers).
>>
is a signed right shift. That means that the leftmost bit (which determines the sign of a number) will be shifted into the left side.
e.g.
1100 >> 2 == 1111
0111 >> 2 == 0001
If you want to do an unsigned shift (which ignores the sign bit), use >>>
which will zero-fill the left end of the bitstring.
Bitwise operators work on 32 bit integers, while multiplication and division works on floating point numbers.
When you shift a number, it's converted from a floating point number to a 32 bit integer before the operations, and converted back to a floating point number after the operation. The number 2242052096 has the 32nd bit set, so it is a negative number when converted to and from a 32 bit integer.
The >>
right shift operator doesn't change the sign of the value, i.e. the bits that are shifted in from the left have the same value as the sign bit. Use the >>>
right shift operator to shift in zero bits instead.
Reference: MDN: Bitwise operators
(2242052096 / 65536) == (2242052096 >>> 16)
Note the different shift.
Javascript normally represents numbers as (double-precision) floating point.
Almost all bitwise operations convert to a signed 32-bit integer, do whatever they're going to do, then treat the result as a signed 32-bit integer when converting back.
The exception is >>>
which treats the result as an unsigned 32-bit integer when converting back.
So:
>>>
instead of >>
;a * 128
gives the expected answer because it's never converted to a signed 32-bit integer in the first place - it's just a floating-point multiplication;a << 7
gives an unexpected answer because it's converted to a signed 32-bit integer, and then you shift a 1
into the sign bit, resulting in a negative signed 32-bit value.There isn't a <<<
, but if you want to write your left shift as a shift, you can use
(a << 7) >>> 0
to get the expected answer (the >>> 0
effectively casts the signed 32-bit value to an unsigned 32-bit value).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With