5

I am creating a bitmask in javascript. It works fine for bit 0 through 14. When I set only bit fifteen to 1. It yields the integer value of "-2147483648" instead of "2147483648". I can do a special case hack here by returning hardcoded "2147483648" for bit fifteen but I would like to know the correct way of doing it.

Sample code:

function join_bitmap(hex_lower_word, hex_upper_word)
{
    var lower_word = parseInt(hex_lower_word, 16);
    var upper_word = parseInt(hex_upper_word, 16);
    return (0x00000000ffffffff & ((upper_word<<16) | lower_word));
}

Above code returns -2147483648 when hex_lower_word is "0x0" and hex_upper_word is "0x8000" instead of 2147483648

F Yaqoob
  • 2,471
  • 2
  • 15
  • 16
  • You mean MSB 15 or LSB 15? – ATOzTOA Feb 04 '13 at 19:33
  • 5
    The results of Javascript bitwise operations are always [signed 32-bit integers](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators#Signed_32-bit_integers). –  Feb 04 '13 at 19:36

3 Answers3

3

The reason for this is because Javascript's bit shift operations use signed 32-bit integers. So if you do this:

0x1 << 31   // sets the 15th bit of the high word

It will set the sign bit to 1, which means negative.

On the other hand instead of bit shifting you multiply by powers of two, you'll get the result you want:

1 * Math.pow(2, 31)
robbrit
  • 17,560
  • 4
  • 48
  • 68
2

The reason is, you are setting the sign bit...

2147483648 is 1 followed by 31 zeros in binary...

As you are doing a bitwise operation, the output is always a signed 32 bit number, which makes the 32nd bit the sign bit, so you get a negative number...

Update

(upper_word * Math.pow(2, 16))

will give positive 2147483648.

But, you still have the OR operation, which puts us back to square one...

ATOzTOA
  • 34,814
  • 22
  • 96
  • 117
1

As previous answers explained, the bitwise operators are 32 bit signed. Thus, if at any point along the way you set bit 31, things will go badly wrong.

In your code, the expression

(upper_word<<16) | lower_word)

is evaluated first because of the parentheses, and since upper_word has the top bit set, you will now have a negative number (0x80000000 = -2147483648)

The solution is to make sure that you do not shift a 1into bit 31 - so you have to set bit 15 of the upper word to zero before shifting:

mask15 = 0x7fff;
((upper_word&mask15)<<16|lower_word)

This will take care of "numbers that are too big become negative", but it won't solve the problem completely - it will just give the wrong answer! To get back to the right answer, you need to set bit 31 in the answer, iff bit 15 was set in upper_word:

bit15 = 0x8000;
bit31 = 0x80000000;
answer = answer + (upper_word & bit15)?bit31:0; 

The rewritten function then becomes:

function join_bitmap(hex_lower_word, hex_upper_word)
    {
        var lower_word = parseInt(hex_lower_word, 16);
        var upper_word = parseInt(hex_upper_word, 16);
        var mask15 = 0x7fff;
        var bit15 = 0x8000;
        var bit31 = 0x80000000;
        return 0xffffffff & (((upper_word&mask15)<<16) | lower_word) + ((upper_word & bit15)?bit31:0);
    }

There isn't just a single "hard coded special case" - there are 2 billion or so. This takes care of all of them.

F Yaqoob
  • 2,471
  • 2
  • 15
  • 16
Floris
  • 45,857
  • 6
  • 70
  • 122
  • Brilliant solution. I am impressed! Thanks a lot. – F Yaqoob Feb 04 '13 at 21:00
  • 1
    Thanks for the compliment! Manipulating bits is a bit of a hobby of mine... if you liked this, you might want to look at [this recent answer](http://stackoverflow.com/questions/14547087/extracting-bits-with-a-single-multiplication/14547307#14547307) – Floris Feb 04 '13 at 21:20
  • @FYaqoob - Thanks for catching the sloppy mistakes and taking the time to fix them! – Floris Feb 05 '13 at 19:52