Bit Manipulation Fundamentals

July 11, 2026 · 3 min read

Bitwise operations feel like a relic from a lower-level era, but they show up constantly: flags and permissions, hashing, graphics, compression, and a whole genre of interview problems. The operators are simple; the leverage comes from a few recurring tricks. Let's build them up.

The operators

Every integer is a row of bits. The bitwise operators work on those bits in parallel:

OpNameRule per bit
&AND1 only if both bits are 1
|OR1 if either bit is 1
^XOR1 if the bits differ
~NOTflips every bit
<<left shiftshift bits up (× 2 per step)
>>right shiftshift bits down (÷ 2 per step)

XOR is the interesting one. It's "difference," and it has magic properties: x ^ 0 === x, x ^ x === 0, and it's its own inverse. Those three facts power a surprising number of tricks.

Reading, setting, and clearing a bit

The basic vocabulary — to work with bit i, build a mask 1 << i (a number with a single 1 in position i) and combine it:

const isSet   = (n, i) => (n & (1 << i)) !== 0; // test bit i
const setBit  = (n, i) => n | (1 << i);         // force bit i to 1
const clear   = (n, i) => n & ~(1 << i);        // force bit i to 0
const toggle  = (n, i) => n ^ (1 << i);         // flip bit i

This is exactly how permission flags work: READ | WRITE packs multiple booleans into one integer, and flags & WRITE checks one.

Trick 1: n & (n − 1) clears the lowest set bit

Subtracting 1 flips the lowest 1 bit to 0 and turns everything below it to 1; AND-ing with the original wipes out that lowest bit and everything under it. Repeat until zero and you've counted the set bits — running once per set bit, not once per bit:

0
7
0
6
0
5
1
4
0
3
1
2
1
1
0
0
n = 22count = 0

Count the set bits in 22 = 00010110. The trick: n & (n − 1) always clears the lowest set bit.

0 / 4
function countBits(n) {
  let count = 0;
  while (n) {
    n &= n - 1;   // remove the lowest set bit
    count++;
  }
  return count;
}

For a number with only a few bits set, this is far faster than checking all 32 positions. (n & (n - 1)) === 0 is also the classic power-of-two test — a power of two has exactly one set bit.)

Trick 2: XOR finds the unique element

Every number XOR'd with itself is 0, and XOR is commutative. So if every value in an array appears twice except one, XOR-ing them all together cancels the pairs and leaves the loner — O(n) time, O(1) space, no hash set:

function singleNumber(nums) {
  return nums.reduce((acc, n) => acc ^ n, 0);
}
singleNumber([4, 1, 2, 1, 2]); // 4

The same idea swaps two variables without a temp (a ^= b; b ^= a; a ^= b) and detects the one changed bit between two numbers (a ^ b).

A warning specific to JavaScript

JavaScript's bitwise operators coerce their operands to 32-bit signed integers, even though numbers are normally 64-bit floats. That has two consequences:

  • Bit operations silently break for values above 2³¹ − 1. For big integers, use BigInt (whose bitwise operators have no width limit).
  • >> is an arithmetic shift (preserves the sign bit); >>> is a logical shift (fills with zeros). Mixing them up on negative numbers is a classic bug.
-8 >> 1;   // -4  (sign preserved)
-8 >>> 1;  // 2147483644  (treated as unsigned)

Wrap-up

Bit manipulation is a small vocabulary — masks with 1 << i, and the AND / OR / XOR / shift operators — plus a few tricks worth memorizing: n & (n-1) strips the lowest set bit, XOR cancels pairs and finds the odd one out, and a single set bit means a power of two. Reach for them when you need packed flags or a constant-space answer, and mind JavaScript's 32-bit coercion.