0

I have value 0xAA which I want to shift 24 bits left, to give me 0xAA000000. Instead, I get a negative value (-0x56000000).

var value = 0xAA;
var shifted = value << 24; // -0x56000000
var expected = 0xAA000000; // 0xAA000000

(expected === shifted) // false

This occurs in both Node.JS and Chrome.

James Monger
  • 10,181
  • 7
  • 62
  • 98
  • The title refers to a 16-bit integer - are you sure that's the size? And if that is the intended size, why do you want to shift it 24 bits? – Ben S. Apr 30 '17 at 19:27
  • Sorry @BenS. I meant a 32 bit integer. – James Monger Apr 30 '17 at 19:28
  • Possible duplicate of [Bitwise operations on 32-bit unsigned ints?](http://stackoverflow.com/questions/6798111/bitwise-operations-on-32-bit-unsigned-ints) – Raymond Chen Apr 30 '17 at 19:46
  • The important part with bit operations is, they deal with a 32bit **signed** integer. The bit at `0x80000000` contains the sign! You can convert this back into an uint32 with `function uint(value){ return value >>> 0 }`. Although the value is technically now a Number (64bit float). – Thomas Apr 30 '17 at 19:51

1 Answers1

3

It's treating the value as signed, so the top bit is used as the sign bit. Since 0xAA000000 has the top bit set, you're winding up with a negative number. Like most machines, it seems to be representing signed values in two's complement.

In languages that offer them, unsigned values are typically preferred for these kinds of bit operations, because they aren't subject to these concerns. In JavaScript, I'd typically use a library like BigInteger.js to handle integer values outside the normal signed integer range. (Technically, a floating-point value like a typical JavaScript Number value should be able to represent any value in the range you're interested in, but those aren't really designed for the kinds of bitwise operations that you're trying to perform, so I think a library is going to be easier to work with).

If you definitely always have a 32-bit value (and no larger) and you want an unsigned result, using the zero-fill right shift operator >>> to shift by zero should give the correct result.

var value = 0xAA; var shifted = (value << 24) >>> 0; // 0xAA000000

Ben S.
  • 1,133
  • 7
  • 7
  • How can I work around this? I need to get `0xAA000000` from `0xAA`. – James Monger Apr 30 '17 at 19:35
  • Well, that is the bit pattern inside `shifted`, it's just interpreted as a large negative number. What are you trying to do with this value? – Ben S. Apr 30 '17 at 19:37
  • I am trying to convert an array of 4 8-bit values into a 32-bit integer – James Monger Apr 30 '17 at 19:40
  • Okay, if you specifically want unsigned integers (that is, the 4 8-bit values always represent a positive number in the range 0 to 4,294,967,295) and you require exact precision rather than the approximations that you'd get from floating-point values, then you probably want to use something like the BigInteger.js library I mention above. – Ben S. Apr 30 '17 at 19:43
  • Using a zero fill right shift seems to work as well: `console.log(((value << 24) >>> 0).toString(2) === (expected).toString(2));` – philip yoo Apr 30 '17 at 19:47
  • @JamesMonger `I am trying to convert an array of 4 8-bit values into a 32-bit integer` signed or unsigned? That's the part you're missing. It's a 32bit **signed integer** – Thomas Apr 30 '17 at 20:02
  • @Thomas 32-bit unsigned sorry – James Monger Apr 30 '17 at 20:06