
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: 130)