Bit manipulation: masks, shifts, and the n & (n-1) trick
Six bitwise operators plus a one-bit mask give O(1) idioms to test, set, clear, and toggle bits. Power-of-two is n & (n-1) == 0; popcount is Brian Kernighan's n &= n-1 loop, O(set bits). In JS, bitwise ops are 32-bit signed, so use >>> for unsigned.
A permissions field holds eight flags: can-read, can-write, can-delete, and five more. You could store eight booleans, an array, or a set. Or you store one byte and let each bit be one flag. Now “does this user have write access?” is a single machine instruction, “grant write” is another, and the whole permission set fits in a register the CPU compares in one cycle. This is not a micro-optimization trick from a bygone era — it is how Unix file modes, network protocol headers, chess engines, bloom filters, and database column bitmaps all work. Underneath every integer is a row of bits, and a handful of operators let you read and rewrite those bits directly. This lesson is the toolbox: the six bitwise operators, the mask idioms built from them, and two classics — checking a power of two and counting set bits — that show the idioms in action.
After this lesson you can name the six bitwise operators and say exactly what each does bit-by-bit: AND (&) keeps a bit only where both inputs have it, OR (|) keeps a bit where either does, XOR (^) keeps a bit where they differ, NOT (~) flips every bit, and the two shifts (<<, >>) slide the bit pattern left or right. You can build the four mask idioms from those operators — test bit i with (x >> i) & 1, set it with x | (1 << i), clear it with x & ~(1 << i), and toggle it with x ^ (1 << i) — and explain why a single shifted 1 (a one-hot mask) is the common ingredient. You can write the power-of-two check n > 0 && (n & (n - 1)) === 0 and explain why subtracting one and ANDing erases the lowest set bit. You can implement Brian Kernighan’s population count (the Hamming weight) and state its cost: O(number of set bits), not O(word size). And you know the one JavaScript trap that bites everyone: bitwise operators coerce their operands to 32-bit signed integers, so 1 << 31 is negative and you reach for >>> (or >>> 0) when you want unsigned 32-bit results.
Every integer is a row of bits
The number 13 is not really “thirteen” inside the machine — it is the bit pattern 1101 (8 + 4 + 0 + 1). Bit manipulation means operating on those individual bits directly, instead of on the number’s decimal value. The operators that do this are the bitwise operators, and there are exactly six worth memorizing.
The six operators
Before you can build anything useful, you need to know what each operator actually does to the bits — because mixing them up (using AND where OR belongs) is the most common source of subtle, hard-to-debug flag bugs. Each operator below acts on every bit position independently — bit i of the result depends only on bit i of the inputs.
- AND (
&) — result bit is1only if both input bits are1. Used to read or clear bits:1100 & 1010 = 1000. - OR (
|) — result bit is1if either input bit is1. Used to set bits:1100 | 1010 = 1110. - XOR (
^) — result bit is1only if the input bits differ. Used to toggle bits and to find differences:1100 ^ 1010 = 0110. XOR is its own inverse:x ^ y ^ y === x. - NOT (
~) — flips every bit (unary).~1100flips all bits, including the high ones you usually do not see. - Left shift (
<<) — slides bits left, filling with zeros on the right.1 << 3 = 1000(decimal8). Each left shift by one multiplies by two. - Right shift (
>>) — slides bits right.1101 >> 1 = 110(decimal6). Each right shift by one divides by two (toward negative infinity, because in most languages>>copies the sign bit).
Together, AND and OR handle reading and writing; XOR handles toggling and comparison; NOT inverts; the shifts scale. When you see a bit idiom in the wild, one of these six is always at its core.
The one-hot mask: the key building block
Almost every bit trick starts with a mask — a number whose bits select which positions you care about. The most useful mask is a single 1 shifted into position i: 1 << i. That gives a “one-hot” pattern with exactly one bit set, at position i. Combine it with an operator and you get the four idioms that do almost everything:
test bit i: (x >> i) & 1 -> 0 or 1
set bit i: x | (1 << i) -> forces bit i to 1
clear bit i: x & ~(1 << i) -> forces bit i to 0
toggle bit i: x ^ (1 << i) -> flips bit iTo set a bit, OR with a mask that has that bit on. To clear a bit, AND with a mask that has that bit off (~(1 << i) is all ones except position i). To toggle, XOR with the one-hot mask. To test, shift the bit you want down to position 0 and mask off the rest with & 1. Four operators, one mask, every single-bit operation.
Classic 1: is n a power of two?
When you see a size or alignment check in a memory allocator or a hash-table resize, this trick is almost always hiding inside. A power of two has exactly one bit set: 1, 10, 100, 1000, ... (decimal 1, 2, 4, 8). Here is the trick that checks it in O(1) with no loop:
n & (n - 1) === 0 (for n > 0)Why does it work? Subtracting 1 from a number flips its lowest set bit to 0 and turns every bit below it into 1. So 1000 - 1 = 0111. ANDing the two — 1000 & 0111 — has no bit in common, giving 0. If n had more than one bit set, the higher bits survive the AND and the result is non-zero. The n > 0 guard matters: 0 & (0 - 1) is also 0, but 0 is not a power of two.
Classic 2: counting set bits (Hamming weight)
The Hamming weight of a number is how many of its bits are 1. The naive way checks all 32 positions. Brian Kernighan’s algorithm is smarter: n &= n - 1 clears the lowest set bit (same n - 1 insight as above), so you loop once per set bit and stop the moment n hits zero:
count = 0
while n != 0:
n &= n - 1 // erase the lowest set bit
count += 1A number with three set bits costs three iterations, not thirty-two. That makes the loop O(number of set bits) — cheaper than scanning every position when the number is sparse.
Brian Kernighan’s population count
The whole algorithm is one loop. The trick is on the line that clears the lowest set bit; everything else is bookkeeping.
function popcount(x) { x = x >>> 0; // treat the 32 bits as unsigned let count = 0; while (x !== 0) { x &= x - 1; // clear the lowest set bit count++; } return count; } - L2 In JS, bitwise ops work on 32-bit SIGNED ints. The unsigned right shift >>> 0 reinterprets the bits as an unsigned 32-bit value, so a number with bit 31 set is counted correctly instead of looping on a negative.
- L4 Loop until no set bits remain. A sparse number (few 1s) exits fast; this is what makes it O(set bits), not O(32).
- L5 The heart of it: x - 1 flips the lowest set bit to 0 and turns every bit below it to 1; ANDing with x clears exactly that one bit and leaves the rest untouched.
Run it: masks, power-of-two, and popcount together
The same one-hot mask 1 << i drives every single-bit idiom below. Watch the four mask operations on x = 1010, then the two classics.
Bit 1 of 1010 is 1. Setting bit 0 gives 1011; clearing bit 3 gives 10; toggling bit 2 gives 1110. 16 is 10000 (one bit set) so it is a power of two; 24 is 11000 (two bits) so it is not. 13 is 1101 (three set bits) and 255 is 11111111 (eight). Nothing here loops over decimal values — every line is a couple of bit operations.
Let us trace Brian Kernighan’s popcount on n = 13 (1101), watching n &= n - 1 erase one set bit per step until n reaches zero.
x = 13 (1101), count = 0 x != 0 -> x &= x - 1; count++ x != 0 -> x &= x - 1; count++ x != 0 -> x &= x - 1; count++ x == 0 -> stop return count Single operations: O(1) on a machine word
Each of the six bitwise operators, and each mask idiom (test, set, clear, toggle), is a single CPU instruction on a value that fits in a register. For fixed-width integers (32- or 64-bit) that is O(1) time and O(1) space — no loops, no allocation. The power-of-two check n & (n - 1) === 0 is two operations and a comparison: also O(1).
Counting bits: O(set bits) with Brian Kernighan
Counting set bits is the one place a loop appears, and the two approaches differ:
iterations time
naive (test each position) one per bit width O(w) (w = 32 or 64)
Brian Kernighan (n &= n-1) one per SET bit O(popcount(n)) <= O(w)Brian Kernighan’s loop runs once per 1-bit, so a sparse number (say one bit set) costs one iteration, while the naive scan always costs w. In the worst case — every bit set — both are O(w), but Kernighan is never worse and usually better.
The “O(1)” caveat: word size is a constant only if it is fixed
Calling bit operations “O(1)” assumes the integer fits in one machine word. That holds for 32-bit and 64-bit ints. The moment you work with arbitrary-precision integers (JavaScript BigInt, Python int, big-integer libraries), a value spans many words, and a bitwise op over a b-bit number is O(b / word size) = O(b) — linear in the number of bits, not constant. So “bit tricks are free” is true for fixed-width integers and false for big integers.
Hardware note
Most CPUs have a dedicated POPCNT instruction that counts set bits in true O(1), and compilers expose it (C++ std::popcount, GCC __builtin_popcount). Brian Kernighan’s algorithm is the portable fallback when no such instruction (or intrinsic) is available — for example in plain JavaScript.
Which operator and mask SET bit i of x to 1 (leaving the other bits unchanged)?
What does x & ~(1 << i) do?
Why does n & (n - 1) === 0 (with n > 0) test for a power of two?
Brian Kernighan's popcount runs n &= n - 1 in a loop. How many iterations does it take for n = 13 (binary 1101)?
In JavaScript, why does popcount start with x = x >>> 0?
What is the decimal value of 1 << 5 ?
How many set bits (Hamming weight) does the decimal number 255 have?
▸Why this works
Why store data in bits at all? Three reasons keep bit manipulation in daily use. (1) Space: a set of up to 64 boolean flags fits in a single 64-bit integer instead of 64 separate booleans — bitmasks, bitsets, and bloom filters all exploit this. (2) Speed: testing or combining whole flag sets is one instruction; a chess engine represents the board as 64-bit “bitboards” and computes all knight moves with a few shifts and ORs. (3) Protocols and formats: Unix file permissions, IPv4 headers, PNG chunks, and database column bitmaps pack fields into fixed bit ranges, so reading them requires masking and shifting. Knowing the idioms is the difference between treating these as opaque magic numbers and manipulating them directly.
▸Common mistake
The XOR-swap trap. You can swap two integers with no temporary variable using XOR: a ^= b; b ^= a; a ^= b;. It is a fun party trick, but it has a fatal edge case — if a and b are the same variable or alias the same memory (swap(arr[i], arr[j]) with i === j), the first a ^= b sets that location to 0, and the value is destroyed. A normal temporary-variable swap has no such hazard and modern compilers make it just as fast. Use XOR-swap to understand XOR; do not ship it.
▸Edge cases
Two guards everyone forgets. First, the power-of-two check needs the n > 0 guard: 0 & (0 - 1) is 0, so without it 0 reports as a power of two (it is not). Second, in JavaScript n & (n - 1) only works within 32 bits, because the operands are coerced to 32-bit signed integers — for a value above 2^31 you need BigInt and big-integer bit operations. The clean algebra of bit tricks always assumes a known, fixed bit width; cross that boundary and the language’s integer model takes over.
▸More practice
The single-bit cheat sheet. Keep these four in muscle memory; the one-hot mask 1 << i is the shared ingredient. Test: (x >> i) & 1. Set: x | (1 << i). Clear: x & ~(1 << i). Toggle: x ^ (1 << i). And two whole-number classics: power of two is n > 0 && (n & (n - 1)) === 0; lowest-set-bit isolation is x & -x (handy for iterating set bits). Almost every interview “bit” question is one of these idioms in disguise.
Explain the four single-bit mask idioms and the operators behind them, the power-of-two check and why it works, Brian Kernighan's bit count and its complexity, and the JavaScript 32-bit signed caveat.
Bit manipulation operates on a number’s individual bits using six operators: AND & (bit set if both), OR | (bit set if either), XOR ^ (bit set if they differ), NOT ~ (flip all), and the shifts << (×2) and >> (÷2).
The one-hot mask 1 << i builds four single-bit idioms:
- Test bit i:
(x >> i) & 1 - Set bit i:
x | (1 << i) - Clear bit i:
x & ~(1 << i) - Toggle bit i:
x ^ (1 << i)
Two classics:
- Power of two:
n > 0 && (n & (n - 1)) === 0— becausen - 1erases the lowest set bit, and a power of two has only that one bit. - Population count (Hamming weight): Brian Kernighan’s
n &= n - 1loop runs once per set bit — O(set bits), not O(word size).
Complexity: single operators and mask idioms are O(1) on a fixed-width machine word; only big integers make them O(bits).
JavaScript caveat: bitwise operators coerce operands to 32-bit signed integers, so 1 << 31 is negative — reach for >>> (or >>> 0) when you want unsigned 32-bit results.
Now when you encounter a flag check, a size constraint, or a popcount loop in a code review, you can name the operator behind it, verify the mask, and spot the missing n > 0 guard before it ships.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.