Javascript NOT is not what you expect


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.
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:

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.

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.

// 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

Larry Battle

I love to program, and discover new tech. Check out my stackoverflow and github accounts.

View Comments

  • why not do this instead?

    var toggleBits = function( dec ){
    return ( dec | 0x80000000 ).toString(2);
    };

    • You're method works too.
      But I was going for a way to invert the binary values starting from the left most 1.

      Now lets compare the two functions with value for ten.
      toggleBits( 10 );

      Your method gives 1111111111111111111111111110101
      My method gives 0101

  • sorry, i forgot to put the one-complement:

    var toggleBits = function( dec ){
    return ( ~(dec | 0x80000000) ).toString(2);
    };

  • this is only necessary for displaying the number in binary using the toString method. The number is stored perfectly fine in memory. It is more a consequence of the toString's implementation.

Share
Published by
Larry Battle

Recent Posts

What really is Data Science? Told by a Data Scientist

What REALLY is Data Science? Told by a Data Scientist - By Joma Tech

7 years ago

Video: How Water Towers Work

How Water Towers Work - Practical Engineering

7 years ago

Dev Tip: Simple tips to improve code reviews

Writing perfect code is a challenging process. That's where code reviews come in to help…

7 years ago

Video: How AI will change the 3d industry

"The Next Leap: How A.I. will change the 3D industry - Andrew Price - Blender"

7 years ago

Best Software Presentation for 2018

"Captain Disillusion: World's Greatest Blenderer - Live at the Blender Conference 2018 - CaptainDisillusion"

7 years ago

Dev Video: A Few Linux Shell Tips

My 5 Favorite Linux Shell Tricks for SPEEEEEED (and efficiency) - By tutoriaLinux > What's…

7 years ago