Bitwise operators in JavaScript
Bitwise operators act on the level of individual bits which allows for fast comparisons and calculations. I’ll be going over a couple of ways to use bitwise opeartors in JavaScript since it’s not entirely clear at first. Some of the examples shown are from various sources which I’ve accumlated over time in which I’m going to elaborate on them.
Bitwise AND
Return a one if the two bits are both one.
5 & 7 // 5
00000101 // 5
00000111 // 7
--------
00000101 // 5
Check if number is even or odd:
((n & 1) === 0) // true if even
((1 & 1) === 0) // false
((2 & 1) === 0) // true
The how:
// 1 & 1
00000001 // 1
00000001 // 1
--------
00000001 // 1 (odd)
// 2 & 1
00000010 // 2
00000001 // 1
--------
00000000 // 0 (even)
Get the difference between two hex values:
var a = 0xF0; // 240
var b = 0xFF; // 255
a & b // 240
~a & b // 15 is the difference
The how:
(240).toString(2) // "11110000"
(255).toString(2) // "11111111"
11110000 // 240
11111111 // 255
--------
11110000 // 240
(~240).toString(2) // "-11110001"
(~240 >>> 0).toString(2) // "11111111111111111111111100001111"
00001111 // ~240 = -241
11111111 // 255
--------
00001111 // 15
The tilde "~" is the bitwise NOT which inverts the bits.
">>>" is the zero-fill right shift operator which converts to a 32 bit unsigned value.
Another way to look at it:
NOT(F0) && FF = 0F && FF = 0F
(240 >>> 0).toString(16) // "f0"
(255 >>> 0).toString(16) // "ff"
~('0xF0' >>> 0) // -241
(-241 >>> 0).toString(16) // "ffffff0f"
FFFFFF0F // -241
000000FF // 255
--------
0000000F // 15
Bitwise OR
Return a one if one of the two bits is a one.
5 | 7 // 7
The how:
00000101 // 5
00000111 // 7
--------
00000111 // 7
Fast truncation:
1.4 | 0 // 1
-1.4 | 0 // -1
Mask a flag additively:
var mask = 0x0003;
var flag = 0xFFF0;
var result = flag | mask; // 65523
result.toString(16); // "fff3"
The how:
0000000000000011 // 3
1111111111110000 // 65520
----------------
1111111111110011 // 65523
(65523).toString(16); // "fff3"
Mask a flag subtractively:
var mask = 0x0003;
var flag = 0xFFFF;
var result = flag & ~mask;
result.toString(16); // "fffc"
The how:
(0x003 >>> 0).toString(2)
// 0000000000000000000000000000011
(~0x003 >>> 0).toString(2)
// 1111111111111111111111111111100
0000000000000011 // 0x003
1111111111111100 // ~0x003
-------------------------------
1111111111111100
1111111111110011 // 65523
parseInt('1111111111110011', 2); // 65523
(65523).toString(16); // "fffc"
Bitwise XOR
Return a one if either bit is a one or zero but not both.
5 ^ 7 // 2
The how:
00000101 // 5
00000111 // 7
--------
00000010 // 2
Variable swap:
var a = 1,
b = 2;
a = a ^ b;
b = b ^ a;
a = a ^ b;
// shorthand
a ^= b;
b ^= a;
a ^= b;
// b = 1, a = 2
The how:
'a'.charCodeAt(0) // 97
'b'.charCodeAt(0) // 98
(97).toString(2) // 1100001
(98).toString(2) // 1100010
// a ^= b;
1100001 // 97 (a)
1100010 // 98 (b)
-------
0000011 // 3 (a)
// b ^= a;
1100010 // 98 (b)
0000011 // 3 (a)
-------
1100001 // 97 (b)
// a ^= b;
0000011 // 3 (a)
1100001 // 97 (b)
-------
1100010 // 98 (a)
String.fromCharCode(97); // b = 'a'
String.fromCharCode(98); // a = 'b'
Toggle switch:
var a = 1;
a = a ^ 1; // 0
a = a ^ 1; // 1
a = a ^ 1; // 0
// shorthand
a ^= 1; // 0
a ^= 1; // 1
a ^= 1; // 0
The how:
a ^= 1;
00000001 // 1 (a)
00000001 // 1
--------
00000000 // 0 (a)
a ^= 1;
00000000 // 1 (a)
00000001 // 1
--------
00000001 // 1 (a)
a ^= 1;
00000001 // 1 (a)
00000001 // 1
--------
00000000 // 0 (a)
Determine if two integers have opposite signs:
var x = 1,
y = 1;
(x ^ y) < 0 // false
The how:
00000001 // x
00000001 // y
--------
00000000 // 0, so 0 < 0 = false
var x = 1,
y = -1;
((x ^ y) < 0); // true
The how:
0000000000000000000000000000001 // x = (1).toString(2)
1111111111111111111111111111110 // y = (~1 >>> 0).toString(2)
------------------------------- // ^ two's complement - flips bits and adds one
1111111111111111111111111111111 // -1, so -1 < 0 = true
Bitwise NOT
Inverts the bits.
[x = -(x + 1)]
~5 // -6
The how:
-6 = -(5 + 1)
(5).toString(2)
00000101
// two's complement
(~5 >>> 0).toString(2)
11111111111111111111111111111010
Shorthand indexOf
:
!!~[1].indexOf(2) // false
!!~[1].indexOf(1) // true
The how:
[1].indexOf(2) // -1
[1].indexOf(1) // 0
~-1 // 0
~0 // -1
!!0 // false
!!-1 // true
Bitwise double NOT
Fast truncation:
// for positive numbers it can be a faster substitute for Math.floor()
~~(5.3) // 5
// negative truncation
~~(-5.3) // -5
// If it can't be parsed to a number than result will always be zero
~~('foo') // 0
~~({}) // 0
Shift operators
Shift bits a specified number of bit positions.
8 >> 1 // 4
8 >> 2 // 2
8 << 1 // 16
8 << 2 // 32
The how:
// 8 >> 1
00001000 // 8
00000100 // 4
// 8 >> 2
00001000 // 8
00000010 // 2
// 8 << 1
00001000 // 8
00010000 // 16
// 8 << 2
00001000 // 8
00100000 // 32
Bitwise left shift operator can be a replacement for Math.pow()
when dealing with bases of 2:
Math.pow(2,24) === 1<<24 // true
Decimal to Integer:
4.12 >> 0 // 4
4.12 << 0 // 4
-4.12 >> 0 // -4
-4.12 << 0 // -4
Integer multiplication and division:
// 20 * 2
20.5 << 1 // 40
// 20 / 2
20.5 >> 1 // 10
Hex to RGB:
var hex = '00FF99';
var dec = parseInt(hex, 16); // 65433
var rgb = {};
rgb.r = (dec >> 16) & 0xFF; // 0
rgb.g = (dec >> 8) & 0xFF; // 255
rgb.b = (dec) & 0xFF; // 153
The how:
// rgb.b
(65433).toString(2)
1111111110011001
parseInt('ff', 16) // 255
00000000011111111
1111111110011001 // 65433
0000000011111111 // 255
---------------
0000000010011001
parseInt('10011001', 2) // 153
RGB to Hex:
var r = 0, g = 255, b = 153;
var hex = '#' +
(
(1 << 24) +
(r << 16) +
(g << 8) + b
)
.toString(16)
.substr(1);
hex // "#00ff99"
Zero-fill Right Bit Shifting
Shifts the operand a specified number of bits to the right and zero bits are shifted in from the left. Sign bit is zero so result is always a 32-bit unsigned integer.
5 >>> 0 // 5
"5" >>> 0 // 5
"hello" >>> 0 // 0
-2 >>> 0 // 4294967294
3.6 >>> 0 // 3
true >>> 0 // 1
0xFF >>> 1 // 127
The how:
5 >>> 0
00000101 // 5
(-2 >>> 0).toString(2)
'11111111111111111111111111111110' // 4294967294
0xFF >>> 1
parseInt('0xFF', 16).toString(2)
111111111 // 255
011111111 // 127
Getting creative
Finding the smallest number:
var x = 9;
var y = 5;
var smallest = y ^ ((x ^ y) & -(x < y));
smallest // 5
The how:
00001001 // x = (9 >>> 0).toString(2)
00000101 // y = (5 >>> 0).toString(2)
(x ^ y)
00001001
00000101
--------
00001100 // parseInt('00001100', 2) = 12
-(x < y)
-(false)
-0
0
5 ^ (12 & 0)
(12 & 0)
00001100 // (12 >>> 0).toString(2)
00000000 // 0
--------
00000000 // 0
(5 ^ 0)
00000101 // 5
00000000 // 0
--------
00000101 // parseInt('00000101', 2) = 5
Finding the largest number:
var x = 9;
var y = 5;
var largest = x ^ ((x ^ y) & -(x < y));
largest // 5
The how:
00001001 // x = (9 >>> 0).toString(2)
00000101 // y = (5 >>> 0).toString(2)
(x ^ y)
00001001
00000101
--------
00001100 // parseInt('00001100', 2) = 12
-(x < y)
-(false)
-0
0
9 ^ (12 & 0)
(12 & 0)
00001100 // (12 >>> 0).toString(2)
00000000 // 0
--------
00000000 // 0
(9 ^ 0)
00001001 // 9
00000000 // 0
--------
00001001 // parseInt('00001001', 2) = 9
If number is power of 2:
var v = 8;
var isPowerOf2 = v && !(v & (v - 1));
isPowerOf2 // true
The how:
(v - 1)
(8 - 1)
7
(v & 7)
00001000 // (8).toString(2)
00000111 // (7).toString(2)
--------
00000000
!(0)
true
v && 0
(8 && true)
(!!8 && true)
(true && true)
true
Examples stolen from Bit Twiddling Hacks by Sean Eron Anderson.
Create array of bits of a number:
var index, bitMask;
var num = 5;
var array = [];
for (bitMask = 128, index = 0; bitMask > 0; bitMask >>= 1, index++) {
array.push((bitMask & num ? 1 : 0 ));
}
array // [0, 0, 0, 0, 0, 1, 0, 1]
Stolen from Getting Bitwise with JavaScript by Rebecca Murphey.
Conversions
Binary to Decimal:
function binaryToDecimal(binaryString) {
return parseInt(binaryString, 2);
}
binaryToDecimal('11111111'); // 255
Decimal to Binary:
function decimalToBinary(decimal) {
return (decimal >>> 0).toString(2);
}
decimalToBinary(255); // "11111111"
Decimal to Hexadecimal:
function decimalToHex(decimal) {
return (decimal).toString(16);
}
decimalToHex(255); // "FF"
Hexadecimal to Decimal:
function hexToDecimal(hex) {
return parseInt(hex, 16);
}
hexToDecimal('FF'); // 255
Binary to Hexadecimal:
function binaryToHex(binaryString) {
return parseInt(binaryString, 2).toString(16);
}
binaryToHex('11111111'); // "ff"
Hexadecimal to Binary:
function hexToBinary(hex) {
return (parseInt(hex, 16)).toString(2);
}
hexToBinary('FF'); // "11111111"
Binary to Character:
function binaryToChar(binaryString) {
return String.fromCharCode(parseInt(binaryString, 2));
}
binaryToChar('01000001'); // "A"
Character to Binary:
function charToBinary(char) {
return (char.charCodeAt(0)).toString(2);
}
charToBinary('A'); // "01000001"
Conclusion
There are definitely some interesting things you can do with bitwise operators. They are not commonly used due to how hard it can be to wrap your head around them, but bitwise operators can definitely improve performance of your application since they are technically faster than using built in methods. The only downside is it makes the code a little less unreadable. But it might be worth it depending on your needs.
I’d love to see other creative uses of bitwise operators, share if you may.