Javascript NOT is not what you expect

Not gate
Not gate

A weird part of javascript is trying to toggle the bits in a number. For most programming languages, like C and Java, ~ represents a bitwise NOT operation, aka one’s complement. But in javascript, ~ produces the two’s complement. Since ~num gives the two’s complement, equivalent to -(num + 1), then one would suspect that ~(num -1) should give back the one’s complement.
However that’s not the case, as the next example demonstrates.

1
2
3
4
var num = 9;
num.toString(2);            //returns 1001
(~num).toString(2);	// returns "-1010"
~(num - 1).toString(2);    //returns "-1001"

As you can see from the example, there are two main problems. The first is that the sign bit is replaced with a negative sign. And second, the bits don’t get toggled.
To fix this problem most programmers would just XOR the number with a mask to toggle the bits. Although this works, it quickly produces the unintended result once you XOR a number larger than the mask.
Example:

1
2
3
4
5
var BIT_MASK = 0xF;
(9).toString(2);			//returns "1001"
(9 ^ BIT_MASK).toString(2);		//returns "110"
(30).toString(2);			//returns "11110"
(30 ^ BIT_MASK).toString(2);		//returns "10001"

A simple solution to this problem is to not to use javascript for bitwise operations.
However, if javascript is required then use the following code to implement bitwise NOT operation.
Method 1: Simple approach.

1
2
3
4
5
6
7
8
9
var toggleBits = function( dec ){
    var mask = 0xFFFFFFF;
    return ( dec ^ mask ).toString(2).match( RegExp( "[01]{"+(dec.toString(2).length)+"}$" ) )[0];
};
 
var num = 23;
num.toString(2);		// returns 10111
num = toggleBits(num);	        // num = 01000;
num = parseInt( num, 2);	// num = 8, binary = 1000

Method 2: More advance features.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Programer: Larry Battle
// Purpose: Provide a bit toggle function for javascript.
var getStrCopy = function (str, copies) {
	var newStr = str;
	copies = (copies > 0) ? copies : 1;
	while (--copies) {
		newStr += str;
	}
	return newStr;
};
var convertDecToBase = function ( dec, base, length, padding ) {
	padding = padding || '0' ;
	var num = dec.toString( base );
	length = length || num.length;
	if (num.length !== length) {
		if (num.length > length) {
			throw new Error("convertDecToBase(): num(" + num + ") > length(" + length + ") too long.");
		}
		num = getStrCopy( padding, (length - num.length)) + num;
	}
	return num;
};
var formatBinaryStr = function( str ){
    return str.replace( /\d{4}/g, '$& ' ).replace( /\s$/,'');
};
var toggleBits = function( dec, length, doFormat ){
    var str = convertDecToBase( dec, 2, length || 8 );
    var binaryStr = str.replace( /0/g, 'o' ).replace( /1/g, '0').replace( /o/g, '1' );
    return ( doFormat ) ? formatBinaryStr( binaryStr ) : binaryStr ;
};
 
// The following requires Firebug or Google Chrome Dev Tools
clear();
 
// Test case
var num = 230;
var testNum = parseInt( toggleBits(num), 2 );
testNum = parseInt( toggleBits( testNum ), 2 );
console.log( "(Test Case) 2 Bit Toggles will give the original number: " +  (testNum == num) );
 
// Examples: 
console.log( toggleBits( 1 ) );    // displays "11111110"
console.log( toggleBits( 2 ) );    // displays "11111101"
console.log( toggleBits( 50, 16 ) );// displays "1111111111001101"
console.log( toggleBits( 15, 8, true ) );    // displays "1111 0000"
console.log( toggleBits( 520, 16, true ) ); // displays "1111 1101 1111 0111"

For more information check out Mozilla’s Bitwise Operators Reference

(Page view Count: 116)