15 bit manipulation problems that show up in software engineering interviews at Google, Meta, Amazon, and fintech firms. Each has a solution with an explanation of the underlying trick, not just the code.
A power of two in binary is a single 1 followed by zeros: 1 (1), 2 (10), 4 (100), 8 (1000). The trick: n & (n - 1) clears the lowest set bit. If the result is 0, exactly one bit was set.
function isPowerOfTwo(n) {
return n > 0 && (n & (n - 1)) === 0;
}
// 8 (1000) & 7 (0111) = 0 → true
// 10 (1010) & 9 (1001) = 8 → false
The least significant bit determines parity. n & 1 is 0 for even, 1 for odd. Faster than n % 2 because it avoids division. In hot loops, this adds up.
function isEven(n) { return (n & 1) === 0; }
function isOdd(n) { return (n & 1) === 1; }
Brian Kernighan's algorithm: each iteration of n &= (n - 1) clears the lowest set bit. The loop runs exactly once per set bit — so an integer with 3 bits set runs 3 times, not 32.
function countSetBits(n) {
let count = 0;
while (n) {
n &= (n - 1); // Clear lowest set bit
count++;
}
return count;
}
// 13 (1101): clears bit 0→1100, bit 2→1000, bit 3→0000. count=3.
XOR swap: a = a ^ b; b = a ^ b; a = a ^ b;. This works because XOR is its own inverse — a ^ b ^ b = a. In practice, modern compilers optimize a temp-variable swap to the same machine code, so this is more a puzzle than a performance tip.
let a = 5, b = 3; a ^= b; // a = 6 b ^= a; // b = 5 a ^= b; // a = 3
Given an array where every number appears twice except one, find the single number. XOR all elements. Pairs cancel (x ^ x = 0), leaving the singleton. O(n) time, O(1) space.
function singleNumber(nums) {
return nums.reduce((acc, n) => acc ^ n, 0);
}
// [4,1,2,1,2] → 4 ^ 1 ^ 2 ^ 1 ^ 2 = 4
Process bit by bit: shift result left, grab the lowest bit of n, OR it into result, shift n right. Repeat 32 times. This is a standard interview question with no shortcut — you must show you understand bit-by-bit extraction.
function reverseBits(n) {
let result = 0;
for (let i = 0; i < 32; i++) {
result = (result << 1) | (n & 1);
n >>>= 1; // Unsigned shift so sign bit does not infect
}
return result >>> 0; // Convert to unsigned
}
An array of length N contains numbers from 0 to N with exactly one missing. XOR all indices (0 to N) and all array values. The matching pairs cancel, leaving the missing number. Same property as the single-number problem, but with two lists.
function missingNumber(nums) {
let xor = nums.length; // Start at N (highest index)
for (let i = 0; i < nums.length; i++) {
xor ^= i ^ nums[i];
}
return xor;
}
// nums=[3,0,1], N=3: 3 ^ (0^3) ^ (1^0) ^ (2^1) = 2
If two numbers have opposite signs, their XOR is negative (the sign bit is 1). (a ^ b) < 0 is the entire check. This is O(1) with no multiplication or conditional branching.
function oppositeSigns(a, b) {
return (a ^ b) < 0;
}
// 5 ^ -3 → negative → opposite signs ✓
n & (n - 1) clears the lowest 1-bit. This is the core operation behind Kernighan's counting algorithm, power-of-two checks, and several bitmasking designs. Understanding it unlocks a whole class of solutions.
function clearLowestBit(n) {
return n & (n - 1);
}
// 12 (1100) → 8 (1000)
// 7 (0111) → 6 (0110)
// 0 → 0 (no bit to clear, stays 0)
n & -n isolates the lowest 1-bit. In two's complement, -n = ~n + 1. The AND of n and -n leaves only the lowest set bit. This is used in Fenwick trees (Binary Indexed Trees) for efficient prefix sums.
function lowestSetBit(n) {
return n & -n;
}
// 12 (1100) → 4 (0100)
// 7 (0111) → 1 (0001)
When two numbers appear once and all others appear twice, a single XOR gives a ^ b — the XOR of the two singles. The trick: any set bit in a ^ b is a position where a and b differ. Use that bit to partition the array into two groups, then XOR each group independently.
function singleNumber3(nums) {
let xor = nums.reduce((a, b) => a ^ b, 0);
// Find a bit where the two singles differ
const diffBit = xor & -xor; // Isolate lowest set bit
let a = 0, b = 0;
for (const n of nums) {
if (n & diffBit) a ^= n;
else b ^= n;
}
return [a, b];
}
Use XOR for sum-without-carry and AND+shift for carry. Loop until no carry remains. This is how addition circuits work in hardware — a half-adder built in software.
function add(a, b) {
while (b !== 0) {
const carry = (a & b) << 1; // Bits that overflow
a = a ^ b; // Sum without carry
b = carry;
}
return a;
}
// 5+3: a=5^3=6, carry=(5&3)<<1=2
// a=6^2=4, carry=(6&2)<<1=4
// a=4^4=0, carry=(4&4)<<1=8
// a=0^8=8, carry=0 → 8 ✓
For 8-bit reversal, you can do it in 5 steps with shift-and-mask operations and no loop. For 32-bit, extend the same divide-and-conquer approach. This pattern shows up in ARM's RBIT instruction and CRC table generation.
function reverseByte(n) {
n = ((n & 0xF0) >> 4) | ((n & 0x0F) << 4);
n = ((n & 0xCC) >> 2) | ((n & 0x33) << 2);
n = ((n & 0xAA) >> 1) | ((n & 0x55) << 1);
return n & 0xFF;
}
// 0b11010100 → 0b00101011
Every number appears three times except one. Count the 1s at each bit position across all numbers. If a bit's count is not divisible by 3, that bit belongs to the singleton. No XOR trick applies here — you need per-bit counting.
function singleNumber2(nums) {
let ones = 0, twos = 0;
for (const n of nums) {
ones = (ones ^ n) & ~twos;
twos = (twos ^ n) & ~ones;
}
return ones;
}
// Uses a state machine: (ones, twos) tracks (0,0)→(1,0)→(0,1)→(0,0)
Given a power of two, return its position (0-indexed). Use a lookup table (fastest) or binary search on bit positions. In practice, languages provide __builtin_ctz (C), trailing_zeros() (Rust), or Math.clz32() (JS) for this.
function findSetBitPos(n) {
// n must be a power of two
let pos = 0;
while (n > 1) {
n >>= 1;
pos++;
}
return pos;
}
// 16 = 0b10000 → 4
// Faster: use de Bruijn sequence lookup (32-bit)
const MultiplyDeBruijn = 0x077CB531;
const table = [0,1,28,2,29,14,24,3,30,22,20,15,25,17,4,8,
31,27,13,23,21,19,16,7,26,12,18,6,11,5,10,9];
function lsbPos(v) {
return table[((v & -v) * MultiplyDeBruijn) >>> 27];
}
The most frequently asked bitwise interview questions are: check if a number is a power of two (n & (n-1) == 0), count set bits (population count / Hamming weight), find the single non-duplicate number in an array where every other number appears twice (XOR all elements), reverse bits, and swap two numbers without a temporary variable (XOR swap).
A power of two has exactly one bit set. n & (n - 1) clears the lowest set bit. If the result is zero and n is positive, n is a power of two: return n > 0 && (n & (n - 1)) == 0. For example, 8 (1000) & 7 (0111) = 0, so 8 is a power of two. 6 (0110) & 5 (0101) = 4 (non-zero), so 6 is not.
XOR has three key properties: a ^ a = 0, a ^ 0 = a, and XOR is commutative/associative. When you XOR all numbers in an array where every element appears twice except one, the pairs cancel out (x ^ x = 0) and only the single number remains. This runs in O(n) time and O(1) space — no hash map needed.
Run bitwise operations on real numbers to verify your interview solutions. See the binary representation update in real time as you AND, XOR, and shift.