2008-08-23

Bitwise Operations and Unsigned Numbers in Javascript

EDIT (2012-09-28):

I stumbled upon an easier way: just do an unsigned right-shift with zero! Duh!
var a = 0x03000000
var b = (a << 6) >>> 0
console.log(b.toString(16).toUpperCase())
This will yield the expected C0000000.



Bitwise operators in javascript, as I discovered, treat their operands--and consequently their results--as 32-bit signed integers.
The only exception is the unsigned right-shift operator, which is written as >>>.

So, the following causes a big problem:
var a = 0x03000000;
var b = a << 6;
console.log(b.toString(16).toUpperCase());
One would expect to see C0000000; however, the result is actually -40000000. This is because the left-most bit of b is 1, which, when a number is signed, tells javascript that it is negative.
hex: C    0    0    0    0    0    0    0
bin: 1100 0000 0000 0000 0000 0000 0000 0000
So, how can this signed number be reinterpreted as an unsigned number? Let's try... We'll start with 0xC0000000, which is an unsigned integer.
var a = 0xC0000000;
console.log(a.toString(16).toUpperCase());
Then, we'll add 1 by or-ing it in. This should result in 0xC0000001; however, bitwise operations result in signed numbers. So, javascript thinks that it is 0x-3FFFFFFF.
var b = a | 1;
console.log(b.toString(16).toUpperCase());
Now, one must use the unsigned right-shift in javascript to divide by two. This shifts all bits to the right, pads the left with zeros, and drops the bits from the right. The operand and result will be interpreted as unsigned integers.
var c = b >>> 1;
console.log(c.toString(16).toUpperCase());
In order to reverse the previous operation, the number must be multiplied by 2, and the bit lost from the shift must be replaced.
var d = (c * 2) + (b & 1);
console.log(d.toString(16).toUpperCase());

So, basically, stuff gets to be a bit messy when using bitwise operators in javascript.