Community Article

Bit Manipulation Tricks I Keep Forgetting

A working programmer's cheat sheet of bitwise operations: the moves I look up every six months, what each one does, real production uses, and the ones that look clever but I avoid.

Bit Manipulation Tricks I Keep Forgetting

A working programmer's cheat sheet of bitwise operations: the moves I look up every six months, what each one does, real production uses, and the ones that look clever but I avoid.

bit-manipulation
xor-tricks
fundamentals
performance-optimization
math
camilarao

By @camilarao

December 21, 2025

·

Updated May 18, 2026

1,054 views

34

4.3 (9)

Every six months I find myself reaching for a bit-manipulation trick, blanking on the exact incantation, and Googling for it. After about the fifth round of this I made my own list. This article is that list, slightly expanded with the gotchas that always bite me.

I want to be honest about the value here. Most working engineers do not write bitwise code on most days. The moves below are useful when you are: working with bitmasks (feature flags, permissions, set membership), implementing low-level protocols, optimising hot inner loops, doing competitive programming, or reading code that someone else wrote in those contexts. Outside those, you can ship a career without using ^= once. But when you do need them, the alternatives (an array of booleans, a hash set, an enum) are often slower and clunkier, and I want to remember the bit version when it is the right one.

The base operations and what they really do

a & b    // AND: 1 only where both bits are 1
a | b    // OR:  1 where either bit is 1
a ^ b    // XOR: 1 where exactly one bit is 1 (different)
~a       // NOT: flip every bit
a << n   // left shift: multiply by 2^n
a >> n   // right shift: divide by 2^n (arithmetic in JS, signed)
a >>> n  // unsigned right shift (JS-specific; fills with zero)

The XOR is the one that earns the most rent. It has a property the other three do not: a ^ b ^ b == a. XOR-ing twice with the same value cancels out. That property is what makes XOR the basis for half the tricks below.

The tricks I actually use

These are the ones I have used in production at least once and that are clear enough to put in a PR without a half-page comment.

Check if a number is even or odd.

n & 1   // 0 if even, 1 if odd

This is faster than n % 2 on some older architectures and identical on modern ones. I use it because it reads as "look at the lowest bit", which is more honest about what is happening.

Set, clear, toggle, test the kth bit.

n |  (1 << k)   // set bit k
n & ~(1 << k)   // clear bit k
n ^  (1 << k)   // toggle bit k
(n >> k) & 1    // test bit k (returns 0 or 1)

Bitmask flags use this constantly. If you have a Permissions integer with bit 0 = read, bit 1 = write, bit 2 = admin, these four operations are how you mutate and query it. I keep this block as a comment template in my snippets file.

Check if a number is a power of two.

n > 0 && (n & (n - 1)) === 0

The intuition: a power of two has exactly one bit set. Subtracting 1 flips that bit and turns all lower bits on. AND-ing the two gives zero. The n > 0 guard rules out zero (which would also pass) and negative numbers in two's complement.

Clear the lowest set bit.

n & (n - 1)

This drops the rightmost 1-bit. Useful for counting bits the slow way: keep doing this until n is zero, count the iterations.

Get the lowest set bit (its value).

n & -n

In two's complement, -n == ~n + 1, which makes this expression evaluate to the lowest set bit isolated. I use this in Fenwick tree (BIT) implementations.

Find the unique element in an array where everything else appears twice.

function findUnique(arr) {
    let result = 0;
    for (const x of arr) result ^= x;
    return result;
}

XOR everything together. Pairs cancel out (x ^ x == 0), the unique value remains. O(n) time, O(1) space. This is genuinely useful and the bitwise version is shorter and faster than a hash-set version.

The tricks I look up every time

I cannot keep these in my head, and I am not sure anyone should.

Round up to the next power of two.

function nextPow2(n) {
    if (n <= 1) return 1;
    n--;
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8;
    n |= n >> 16;
    return n + 1;
}

The series of OR-shifts smears the highest set bit downward to fill every lower position. After that, n + 1 rolls it over to the next power of two. I look this up about every nine months when I need a buffer-resize calculation.

Count the number of set bits (popcount). Use n &= n - 1 in a loop, counting iterations. Most modern CPUs have a hardware POPCNT instruction; in JS the pragmatic version is n.toString(2).split('1').length - 1, short and clear at the cost of an allocation.

Iterate over all subsets of a bitmask.

let sub = mask;
while (true) {
    process(sub);
    if (sub === 0) break;
    sub = (sub - 1) & mask;
}

This walks every subset of mask exactly once, in decreasing order. I never remember the (sub - 1) & mask line and always have to re-derive it.

The gotchas that always bite me

Three traps I have personally fallen into, in JavaScript and Python and Java in roughly equal measure.

The first is operator precedence. Bitwise operators have lower precedence than comparison operators in C-family languages. if (n & 0xFF == 0) does not do what you think; it parses as if (n & (0xFF == 0)), which is if (n & false), which is if (0). Always parenthesise: if ((n & 0xFF) == 0). I have shipped this bug.

The second is JavaScript's 32-bit truncation. JS numbers are 64-bit floats, but the bitwise operators convert their arguments to signed 32-bit integers, do the operation, and convert back. So (1 << 31) is -2147483648, not 2147483648. If you are working with values larger than 2^31, use BigInt or shift to a representation that does not need bitwise ops. This bug bit me when I tried to encode user IDs as bitmasks and crossed the 32-bit threshold.

The third is arithmetic vs logical right shift. In JavaScript, >> preserves the sign bit (arithmetic shift) and >>> does not (logical shift). So (-1 >> 1) is -1, but (-1 >>> 1) is 2147483647. If you are working with unsigned data, use >>>. If you are working with signed data, use >>. Mixing them up gives wrong answers silently, which is the worst kind of bug.

When I do not reach for bits

I avoid bitwise tricks for code that is not on a hot path. n & 1 is denser than n % 2 === 0 in a context where nobody else on the team is thinking in bits. If a function runs once per request, the constant-factor speedup is irrelevant and the readability cost is real. Reserve bitwise ops for code called millions of times per second, or where the bit-level semantics is the actual point (a permission mask, a bloom filter, a network protocol).

The cheat sheet I keep pinned in my notes

For the eighth time of looking these up, here is the version I now have pinned:

n & 1                  -> low bit (0 or 1)
n | (1 << k)           -> set bit k
n & ~(1 << k)          -> clear bit k
n ^ (1 << k)           -> toggle bit k
(n >> k) & 1           -> read bit k
n & (n - 1)            -> drop lowest set bit
n & -n                 -> isolate lowest set bit
(n & (n - 1)) == 0     -> power of two? (after checking n > 0)
sub = (sub - 1) & mask -> next subset of mask, walking down

That nine-line block is, in my experience, eighty percent of bit manipulation in working code. The other twenty percent is custom or comes from a specific algorithm reference, and I look those up when I need them rather than trying to memorise. There is no shame in keeping a cheat sheet for moves that are objectively non-obvious; the shame is in copy-pasting code you do not understand. As long as I can derive each line from "XOR cancels", "AND tests", "OR sets", "shift moves", I count it as understood, and the cheat sheet is just a typing aid.

Back to Articles