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