2

I am trying to understand how bit-wise operation in JavaScript work, more specifically how the 32 bit number resulting from a bit-wise operation is converted back to a 64 bit JavaScript number. I am getting some strange results when setting the left most bit in a 32 bit number and when the operation overflows.

For example, with the following operation:

0x01 << 31

Would normally result in 0x80000000 if the number was 32 bits long. But when JavaScript converts this number back to a 64 bit value, it padds the leftmost 32 bits with 1 resulting in the value FFFFFFFF80000000.

Similarly, when left shifting 32 bits, thus overflowing a 32 bit integer, with the operation:

0x02 << 32

The number would overflow, and the result value should be 0x00. But the resulting JavaScript number is 0x02.

Are there any specific rules that JavaScript uses for bit-wise operation that I am not aware of? I understand that all bit-wise operations are performed with 32 bit integers, and that JavaScript numbers are 64 bit double precision floating point numbers, but I cannot understand where the extra padding comes from when converting between the two.

Colin Dumitru
  • 3,327
  • 6
  • 30
  • 47
  • 1
    Here is how the Spec states it: http://www.ecma-international.org/ecma-262/5.1/#sec-11.7.1. – VisioN Mar 20 '13 at 07:32

2 Answers2

1
  1. Result of bitwise operators are signed int32's, the sign bit is propagated when they are converted back to Numbers.

  2. You cannot shift by more than 31 bits:

Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.

That is, x<<32 is the same as x<<0.

georg
  • 211,518
  • 52
  • 313
  • 390
1

In JavaScript all numbers are represented using 53 bits. JavaScript uses floating point representation to store all numbers internally, which means that integers are stored as floating point numbers (mantissa has 53 bits). This blog post is a good read on this subject.

So with 53 bits we can represent max 2^53 = 9007199254740992.

Unlike other languages such as C, C#, you cannot use right shift and AND binary operations to extract lower 32 bits and higher 21 bits from 53 bit numbers in JavaScript.

The reason is when we apply binary operator on any number - JavaScript first convert that number to 32 bit signed number, apply the binary operation and return the result. This means any bit that sits in position higher than 32 will be discarded.

I have used following approach to extract the higher (21 bit) and lower (32 bits) portions from a positive number <= 2^53.

var bigNumber = Math.pow(2, 53); // 9007199254740992
var bigNumberAsBinaryStr = bigNumber.toString(2); // '100000000000000000000000000000000000000000000000000000'
// Convert the above binary str to 64 bit (actually 52 bit will work) by padding zeros in the left
var bigNumberAsBinaryStr2 = '';
for (var i = 0; i < 64 - bigNumberAsBinaryStr.length; i++) {
    bigNumberAsBinaryStr2 += '0';
};

bigNumberAsBinaryStr2 += bigNumberAsBinaryStr;

var lowInt = parseInt(bigNumberAsBinaryStr2.substring(0, 32), 2);
var highInt = parseInt(bigNumberAsBinaryStr2.substring(32), 2);

To answer your question on converting back to 64 bit, see below:

Assert((lowInt * Math.pow(2, 32) + highInt) === bigNumber);
  • there are many better ways to [split a 52-bit integer into 20-bit and 32-bit integers](http://stackoverflow.com/q/19213148/995714) – phuclv Dec 10 '16 at 16:50