Roman Bodnarchuk
Roman Bodnarchuk

Reputation: 29727

Bitwise operations with big integers

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?

Upvotes: 11

Views: 4169

Answers (4)

Guffa
Guffa

Reputation: 700322

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

Upvotes: 6

Matthew Slattery
Matthew Slattery

Reputation: 46998

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:

  • right shifts can be made to work simply by using >>> 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).

Upvotes: 1

tskuzzy
tskuzzy

Reputation: 36456

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.

Upvotes: 7

Griffin
Griffin

Reputation: 14624

(2242052096 / 65536) == (2242052096 >>> 16)

Note the different shift.

Upvotes: 2

Related Questions