Skip to content

Bit manipulation

intermediateCommon atGoogleMetaAppleJane StreetCitadelRead firstArraysPractise in the Sandbox →

An integer is a fixed row of bits, and the CPU applies &, |, ^, ~, <<, >> to all of them at once in O(1). That makes an int a tiny set of up to 32/64 flags, gives you identities like x ^ x = 0 (pairs cancel) and x & (x − 1) (drop the lowest 1), and packs state compactly. The traps are width and sign: JS works in 32-bit signed, C# has fixed int/long, Python ints never overflow.

Picture a light-switch panel with 8 switches, numbered 0 on the right to 7 on the left. Switch k is worth 2^k, and the panel’s number is the sum of the switches that are on: 10110100 is 128 + 32 + 16 + 4 = 180.

The operators act on every switch at once:

Operator Rule per bit Everyday use
a & b (AND) 1 if both are 1 mask: keep only some bits; test a bit
a | b (OR) 1 if either is 1 set bits
a ^ b (XOR) 1 if they differ toggle bits; cancel pairs (x ^ x = 0)
~a (NOT) flip every bit build “everything except” masks
a << k slide left k places multiply by 2^k; build 1 << k
a >> k slide right k places floor-divide by 2^k

Negative numbers use two’s complement: −x is ~x + 1, and the top bit means “negative”. In 8 bits, 11111111 is 255 unsigned or −1 signed. Same bits, two readings.

Click bits to change a, pick an operation, and watch the result. Try a & (a − 1) and a & −a on a few values until the patterns click; they appear constantly.

Bit playground (8-bit)
click the bits
76543210
unsigned / signed
a
180 / -76
a − 1
179 / -77
a & (a − 1)
176 / -80
a (bin)0b10110100
result0b10110000
set bits in a4
power of two?no
a & (a − 1): Clears the lowest set bit. a − 1 flips that bit and every 0 below it. In the result row, raised accent cells are bits that differ from a; green = 1; yellow marks bit k.
The problem says… Bit idea
“every element appears twice except one” XOR everything: pairs cancel.
“set of at most ~20-64 small items”, “which subset”, “visited states” An int as a bitmask set: union |, intersection &, membership (m >> i) & 1.
“enumerate all subsets of n items” (n ≤ ~20) Loop mask from 0 to 2^n − 1; bit i says whether item i is in.
“count 1-bits”, “Hamming distance”, “power of two?” popcount, x & (x − 1).
“flags / permissions / options” Each flag is one bit; combine with |, test with &.
“O(1) extra space” on integer data XOR or sign tricks sometimes replace a hash set.
DP over subsets The mask is the state. See advanced DP.
  1. Read bit k: (x >> k) & 1.
  2. Set / clear / toggle bit k: x | (1 << k), x & ~(1 << k), x ^ (1 << k).
  3. Drop the lowest 1: x & (x − 1). Use it to count bits or to test “power of two” (x > 0 and x & (x − 1) == 0).
  4. Isolate the lowest 1: x & −x. Fenwick trees are built on this.
bit tricks.py
def get_bit(x: int, k: int) -> int:
return (x >> k) & 1 # 1. shift bit k down to position 0, mask it
def set_bit(x: int, k: int) -> int:
return x | (1 << k)
def clear_bit(x: int, k: int) -> int:
return x & ~(1 << k)
def popcount(x: int) -> int:
"""Kernighan: each x & (x - 1) removes the lowest set bit. Needs x >= 0 in Python."""
count = 0
while x:
x &= x - 1 # 2. one iteration per set bit, not per bit position
count += 1
return count
def is_power_of_two(x: int) -> bool:
return x > 0 and x & (x - 1) == 0 # 3. exactly one bit set
def lowest_bit(x: int) -> int:
return x & -x # 4. -x = ~x + 1 matches x only at the lowest set bit
if __name__ == "__main__":
assert get_bit(0b1010, 1) == 1 and get_bit(0b1010, 0) == 0
assert set_bit(0b1000, 0) == 0b1001 and clear_bit(0b1111, 2) == 0b1011
assert popcount(0b1011001000) == 4 == (0b1011001000).bit_count() # int.bit_count: 3.10+
assert is_power_of_two(64) and not is_power_of_two(0) and not is_power_of_two(96)
assert lowest_bit(0b101000) == 0b1000
assert ~5 == -6 # Python ints are unbounded two's complement: ~x == -x - 1
assert -1 & 0xFF == 255 # mask to fix a width before printing or counting
print("ok")

x & (x − 1) works because subtracting 1 flips the lowest 1-bit to 0 and every 0 below it to 1; ANDing with the original wipes all of those. So a loop of it runs once per set bit:

Counting set bits: x & (x − 1)
step through
x
0
0
0
0
0
0
1
0
1
1
0
0
1
0
0
0
712
x712
count0
Count the 1-bits of 712. A naive loop checks all 10 bit positions; this one loops once per set bit.
1 / 10
  1. XOR is its own inverse: x ^ x = 0, x ^ 0 = x.
  2. It’s commutative and associative, so order doesn’t matter: a ^ b ^ a = b.
  3. XOR together everything that should pair up; whatever doesn’t pair is left over.
single / missing / differ_in.py
from functools import reduce
from operator import xor
def single(nums: list[int]) -> int:
"""Every value appears twice except one. x ^ x = 0 and x ^ 0 = x, so pairs cancel."""
return reduce(xor, nums, 0) # 1. order doesn't matter: XOR is commutative and associative
def missing(nums: list[int]) -> int:
"""nums holds 0..n with one value missing. XOR all indices and values; the leftover is it."""
acc = len(nums) # 2. start with n, the one index the loop below never produces
for i, x in enumerate(nums):
acc ^= i ^ x # 3. every present value cancels its matching index
return acc
def differ_in(a: int, b: int) -> int:
return (a ^ b).bit_count() # 4. Hamming distance: count the bits that differ
if __name__ == "__main__":
assert single([4, 1, 2, 1, 2]) == 4
assert single([7]) == 7
assert missing([3, 0, 1]) == 2
assert missing([0]) == 1
assert differ_in(1, 4) == 2 # 001 vs 100
print("ok")

This table is where most bit bugs live.

Python TypeScript / JS C#
integer width unbounded (no overflow) number is a 64-bit float; bitwise ops convert to 32-bit signed int 32-bit, long 64-bit, uint/ulong unsigned
1 << 31 2147483648 −2147483648 −2147483648 (int); use 1L << 31 or 1u << 31
~5 −6 −6 −6
logical (unsigned) right shift n/a; mask first: (x & 0xFFFFFFFF) >> k x >>> k x >>> k (C# 11+), or shift a uint
arithmetic right shift >> (floors, even for negatives) >> >> on signed types
popcount x.bit_count() (3.10+), bin(x).count('1') none; loop or SWAR trick BitOperations.PopCount(uint), int.PopCount (.NET 7+)
bit length / leading zeros x.bit_length() Math.clz32(x) BitOperations.LeadingZeroCount, TrailingZeroCount
binary literal / print 0b1010, bin(x), f"{x:08b}" 0b1010, x.toString(2) 0b1010, Convert.ToString(x, 2)
64-bit masks just work BigInt (1n << 40n) long / ulong
Operation Cost Note
& | ^ ~ << >> on a machine word O(1) A single instruction.
popcount (built-in) O(1) Hardware POPCNT on modern CPUs.
Kernighan popcount loop O(set bits) ≤ 32 or 64 iterations.
Python ops on huge ints O(number of bits / 30) Python’s big ints are arrays of 30-bit digits.
bitmask set of n ≤ 64 items O(1) per union/intersect/test versus O(n) for a list, O(1) avg for a hash set but much larger
enumerate all subsets O(2^n) masks Fine up to n ≈ 20 (about a million).
  • Bitmask DP. dp[mask] over subsets of up to ~20 items (assignment problems, travelling salesman on small n). See advanced DP.
  • Iterate submasks. s = mask; while s: …; s = (s − 1) & mask visits every subset of mask without testing each bit.
  • Bitsets. Arrays of 64-bit words for large sets (C# BitArray, Python big ints, JS Uint32Array): 64× less memory than bool[], and set operations work a word at a time.
  • Bit packing. Store several small fields in one integer with shifts and masks: (a << 8) | b, unpack with (x >> 8) & 0xFF. Common in compact keys, flags and wire formats.
  • Gray code. i ^ (i >> 1): consecutive values differ in exactly one bit.
  • Fenwick trees. Index jumps by i & −i give O(log n) prefix sums. See segment trees.
  • Operator precedence. In JS and C#, &, ^ and | bind looser than ==: x & 1 == 0 means x & (1 == 0) (always 0 in JS, a compile error in C#). Python happens to parse it as (x & 1) == 0, but x & 1 + 1 is x & 2 in all three. Always parenthesize.
  • JS’s 32-bit cliff. Bitwise ops silently truncate values above 2^31 − 1. Timestamps in milliseconds, 64-bit ids and large masks break. Use BigInt or arithmetic.
  • Python negative numbers never “end”. while x: x >>= 1 loops forever for negative x (-1 >> 1 is still -1). Mask to a width first.
  • Overflow in C#. 1 << 31 is negative; 1 << 32 is 1. Use long/ulong and 1L << k.
  • ~ isn’t “logical not”. ~1 is −2, which is truthy. Use ! / not for booleans.
  • Signed right shift on negatives rounds toward −∞ (-7 >> 1 == -4). C#’s -7 / 2 truncates to -3, so shift and divide disagree; Python’s -7 // 2 == -4 matches the shift.
  • Ask the width and sign of the input: “Are these 32-bit signed? Can they be negative?” It changes >> vs >>> and loop termination.
  • Explain each trick in words: “x & (x − 1) drops the lowest set bit, so the loop runs once per 1-bit.” Don’t present it as magic.
  • Offer the readable alternative when it’s fair: “a hash set works too; the bitmask is O(1) and fits in a register because there are at most 20 items.”
  • Follow-ups: “what if the values are 64-bit in JS?” (BigInt), “every element appears three times except one” (count bits mod 3), “two numbers appear once” (split by any set bit of their XOR).
Check yourself
pick one
In JavaScript, what is (1 << 31) > 0?
Check yourself
pick one
What does x & (x − 1) == 0 (with x > 0) tell you?
Check yourself
pick one
Values are 32-bit, every one appears exactly twice except one that appears once. Which uses O(1) space?

Classic textbook exercises, not gym questions:

  • Reverse the bits of a 32-bit unsigned integer.
  • Count bits for every number from 0 to n in O(n) (bits[i] = bits[i >> 1] + (i & 1)).
  • Every element appears three times except one: find it with O(1) space.
  • Generate all subsets of a small list with a mask loop, then the Gray-code ordering.