Skip to content

Algorithmic Patterns

Bit manipulation

Operating on the binary representation directly

Every integer in your program is, underneath, a fixed row of bits - for a byte, eight switches that are each 0 or 1. Decimal hides that, but the CPU never forgets it. Bitwise operators let you touch all eight switches at once, in parallel, in one instruction: test a flag, pack a set into a single number, swap two variables with no temporary, count the ones. Learn three verbs - mask, shift, XOR - and a surprising amount of low-level work collapses into a single line.

The idea in one line

A number is a parallel array of booleans. AND keeps a bit only where both operands have it; OR keeps it where either does; XOR keeps it where they differ; NOT flips every one. Because the hardware does all eight (or thirty-two, or sixty-four) positions simultaneously, a loop over a bag of true/false flags becomes one operation on one integer. That integer, chosen to have exactly the bits you care about, is called a mask.

Eight switches, six operators

Write a byte with the most-significant bit on the left, so position is worth . The value is 10101100: . The four logical operators act bit-by-bit, and their entire behaviour fits in one small table - every column is decided independently, one row of switches at a time:

aba & b (AND)a | b (OR)a ^ b (XOR)
00000
01011
10011
11110

NOT (~) needs no table - it flips every bit, so ~00001111 becomes 11110000. The two shifts slide the whole pattern sideways: x << 1 moves every bit up one place - the same as multiplying by two - and the top bit falls off the byte's edge; x >> 1 moves everything down, halving the value and dropping the lowest bit. Shifting by multiplies or divides by , which is why x << 3 is a fast x * 8.

One mask, four verbs

Pick out bit by building the mask 1 << i (a single 1 at position ). Then every single-bit edit is one operation: set it with x | (1<<i), clear it with x & ~(1<<i), toggle it with x ^ (1<<i), and test it with (x >> i) & 1. OR forces on, AND-with-inverse forces off, XOR flips, AND reads. Those four idioms are the entire vocabulary of flags, permissions, and bitsets - keep them at your fingertips for the demo below.

Flip the bits yourself

Two 8-bit operands, A (teal) and B (amber). Click any bit in the A or B row to flip it between 0 and 1, and watch all six results recompute at once - in binary and decimal. The columns line up, so you can read each logical operator straight down. Then take the challenge: build a mask that isolates A's lowest set bit.

Bit-toggle grid - click A / B bits - live AND - OR - XOR - NOT - shifts
Aclick to flip
= 44 0x2C
Bclick to flip
= 26 0x1A
A & Band
00001000
= 8 0x08
A | Bor
00111110
= 62 0x3E
A ^ Bxor
00110110
= 54 0x36
~Anot
11010011
= 211 0xD3
A << 1shift left
01011000
= 88 0x58
A >> 1shift right
00010110
= 22 0x16

Results are held to 8 bits - on A << 1 the top bit falls off the left edge.

Challenge - isolate the lowest set bit of A

Toggle the mask M below so that A & M keeps only A's rightmost 1 and nothing else. There is a one-line trick - try it by hand first, then reveal it.

Myour mask
= 0 0x00
target (A's lowest set bit) 00000100 (4)A & M 00000000 (0)
Not yet - make A & M equal the target below.

Two's complement, and two tricks it unlocks

Place value first: an 8-bit unsigned number is , each bit worth twice its right neighbour. Negatives use two's complement - to form , flip every bit and add one:

That single rule is why the CPU needs only an adder, never a separate subtractor. It also explains the challenge above: the lowest set bit of x is x & (-x). Negating flips every bit up to and including that lowest 1; the carry from ripples through the trailing zeros and stops exactly at it - so that bit stays 1 in both and , while every other shared bit is now opposite. The AND keeps just that one. A twin fact: x is a power of two exactly when it has a single 1-bit, i.e. x & (x - 1) == 0 for , because subtracting one flips the lone bit to 0 and turns the zeros below it into ones - leaving no overlap.

The tricks, in code

Each idiom below is a single expression doing what would otherwise be a loop or a temporary variable. The &=, |=, ^= forms are in-place versions of the operators you just watched.

# --- Masks: edit one bit at position i (see the "one mask, four verbs" callout)
set_bit    = x | (1 << i)     # force bit i to 1
clear_bit  = x & ~(1 << i)    # force bit i to 0
toggle_bit = x ^ (1 << i)     # flip bit i
test_bit   = (x >> i) & 1       # read bit i as 0 or 1

# --- XOR swap: exchange two values with no temporary
a ^= b; b ^= a; a ^= b        # each XOR cancels one operand back out

# --- Single number: every value appears twice except one
def single(nums):
    r = 0
    for v in nums:
        r ^= v                # pairs cancel: v ^ v == 0, v ^ 0 == v
    return r                  # the lone survivor

# --- Count set bits (Kernighan: one pass per 1-bit, not per bit)
def popcount(x):
    c = 0
    while x:
        x &= x - 1            # clears the lowest set bit each pass
        c += 1
    return c

# --- Power of two? exactly one bit set
is_pow2 = x > 0 and (x & (x - 1)) == 0
Core bit-manipulation idioms

Check yourself

You XOR a number with itself. What is the result?

Which test is true exactly when a positive x is a power of two?

Recall: how do you isolate the lowest set bit of x in one expression - and why does it work?

Think about two's complement. Try to state it, then check.

Lock it in

  • A number is a parallel array of booleans: AND, OR, XOR, and NOT act on every bit at once, so a loop over flags collapses to one operation on one integer (a mask).
  • The four single-bit edits: set with x | (1<<i), clear with x & ~(1<<i), toggle with x ^ (1<<i), and test with (x >> i) & 1.
  • Two's complement () lets the CPU subtract with only an adder, and gives x & (-x) = lowest set bit and x & (x-1) == 0 = power of two.
  • XOR cancels pairs (), so XOR-ing a whole list leaves only the value that appears an odd number of times.

Primary source

For the gentle first pass on binary and the bitwise operators, work through Harvard's CS50x - its early weeks build the byte-as-switches mental model directly. When you want the deep end, Sean Anderson's canonical Bit Twiddling Hacks collects branch-free, loop-free implementations of counting bits, finding the lowest set bit, reversing, and dozens more - the reference the tricks above are drawn from.

Ask your teacher

Want to see a bitset used as a real hash set, or how endianness reorders these bytes in memory? Ask and I'll build it. The tie-in worth planting now, traced in bit manipulation to quantization in the LLM course: bit manipulation is the machinery under quantization. An LLM's weights are 32-bit floats; quantization packs them into int8 or int4 - shrinking the model 4-8x - using exactly these operators: mask off a nibble, shift it into place, OR two 4-bit weights into one byte. That packing (plus the un-shift-and-mask on the way out) is how a model that needs a data-centre GPU in float32 fits on a laptop in int4.