Javascript: Debugging Bitwise Operators

Most people are never going to even use bitwise ops, especially in JavaScript. But if you really must, beware all is not what it seems!

Problem 1’s Complement
1’s complement seems slightly off?!
console.log(~0xFF); //gives -256

Explanation
You might then automatically assume that JavaScript implementation of 1’s complement is fundamental borked, a cursory Google might even back up that. Obviously its hard to believe that handling of bitwise ops are so broken in a language that is so widely used, but given that few if any web developers’ would actually need bitwise ops maybe its the case. (There are plenty of stack overflow posts that recommend the abuse of the ~ operator)

Taking a closer look at the ECMA script definition you see the definition of NOT as the following[1]:

      Let expr be the result of evaluating UnaryExpression.
      Let oldValue be ToInt32(GetValue(expr)).
      Return the result of applying bitwise complement to oldValue. The result is a signed 32-bit integer.

Now this is where things get interesting. You would naturally assume if you give the NOT operator a 8-bit value you’d get an 8-bit field back, but this is clearly not the case. If you want to dig even further the real concrete instance of the value is a 64-bit IEEE 754 representation see [2].

So that neatly explains why ~0xff transforms to -256

1111 1111
becomes
0000 0000 0000 0000 0000 0000 1111 1111
apply ~ (NOT)
1111 1111 1111 1111 1111 1111 0000 0000
aka -256

But that still leaves a problem, how can you easily debug a value that isn’t a Number but is represented as one…

If you’ve got this far there is probably a very good reason you’re using bitwise ops, otherwise you might have given up on them altogether. So it would be somewhat handy to look at the real value, and not the Int32 representation, which console.log and friends are going to give a misleading value for.

Getting at the full representation
A nice simple function to show all the bits for the input value.

function fullBinaryString(input){
//coerce javascript to show full 32bit integer underneath.
return (input>>>0).toString(2);
}
view raw bits.js hosted with ❤ by GitHub

>>> Operator coerce the value input a 32-bit representation by right shifting and filling in with zeros, depending on your usage you can also slice the value, to only show the 8-bits that are needed to debug, like so:

function to8bitString(input){
//clamp the value to 8-bits by and with 0xFF
var clampedValue = (input&0xFF).toString(2);
//pad the string to make it easier to read.
return String("00000000" + clampedValue).slice(-8);
}
view raw bits.js hosted with ❤ by GitHub

General Rule
If dealing with bits, clamp the field to the proper bit length before assignment. Using an 8-Bit clamped array is no guarantee that the value will be assigned properly, there are cases where the value can be assigned to zero instead of the proper bit value. For an 8-bit you would use array[i] = bits&0xFF this will make sure the value is appropriately truncated and does not get evaluated as a Number.

[1] http://www.ecma-international.org/ecma-262/5.1/#sec-11.4.8
[2] http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.19

Leave a Reply