Chapter 6: Bit Manipulation and Binary Representation
6.07Beginner6 topics24 min3 interactive animations

Advanced bit arithmetic

Beyond the everyday tricks, a few classic bit algorithms keep appearing: parity, Gray code, bit reversal, addition without +, exponentiation by squaring, and tries over bits. Each shows a different way to think about a number as a row of bits.

Learning goals

  • Compute parity by folding with XOR
  • Generate and invert Gray codes
  • Reverse the bits of an int with masks and shifts
  • Add two ints with only bit operations and detect overflow
  • Raise a number to a power in O(log n) multiplications
  • Use a trie over bits to find a maximum XOR

Prerequisites

Bitwise operatorsCommon bit tricksXOR techniquesRecursion (for exponentiation)
6.07.1

Parity

The parity of a number is whether it has an odd number of 1 bits. XOR is addition modulo 2, so XOR-ing all the bits together gives the parity. Instead of looping over 32 bits, fold the number onto itself: XOR the upper half into the lower half, then the upper half of that, and so on until one bit remains. Step through 182 in the animation: three folds are enough for 8 bits (five for 32).

Parity is used for error detection: a parity bit makes the total number of ones even or odd, so any single flipped bit is detected.

Java
1static int parity(int x) { // 32-bit version
2 x ^= x >>> 16;
3 x ^= x >>> 8;
4 x ^= x >>> 4;
5 x ^= x >>> 2;
6 x ^= x >>> 1;
7 return x & 1;
8}
9// equivalent: Integer.bitCount(x) & 1

Worst

O(log w) for w-bit words

Space

O(1)

Parity by folding with XOR

Interactive · edit the input

Your input

Try

x = 182

17
06
15
14
03
12
11
00
Setup

Parity is whether the count of 1 bits is odd. 182 has 5 set bits. XOR is addition modulo 2, so folding the number onto itself keeps the parity.

Step 1 / 5
Java
1static int parity(int x) { // 8-bit value
2 x ^= x >> 4; // fold the upper half onto the lower
3 x ^= x >> 2;
4 x ^= x >> 1;
5 return x & 1; // 1 if the number of set bits is odd
6}

Live state

0 folds

x

182

No condition evaluated on this step
Execution trace (dry run table)Show 5 steps
StepLineStateWhat happens
1L1x=182Parity is whether the count of 1 bits is odd. 182 has 5 set bits. XOR is addition modulo 2, so folding the number onto itself keeps the parity.
2L2shift=4 x=189XOR with x >> 4: each bit now holds the parity of 8 original bits.
3L3shift=2 x=146XOR with x >> 2: each bit now holds the parity of 4 original bits.
4L4shift=1 x=219XOR with x >> 1: each bit now holds the parity of 2 original bits.
5L5return=1Bit 0 now holds the parity of all 8 bits: 1 (odd number of 1s). Three steps instead of eight.
6.07.2

Gray code

A Gray code lists all n-bit numbers in an order where consecutive values differ in exactly one bit. The binary reflected Gray code of i is i ^ (i >> 1). Run the animation with 3 bits: every new code differs from the previous in a single highlighted bit, and the last code differs from the first in one bit too.

Gray codes are used in rotary encoders (no glitch when a position changes), Karnaugh maps, and puzzle solving. To convert back, XOR together the code shifted right by 1, 2, 3, … until it is zero.

Java
1static int toGray(int i) { return i ^ (i >> 1); }
2
3static int fromGray(int g) {
4 int n = g;
5 while ((g >>= 1) != 0) n ^= g; // n = g ^ (g >> 1) ^ (g >> 2) ^ ...
6 return n;
7}

Gray code: one bit changes at a time

Interactive · edit the input

Your input

Try

Gray codes

empty
Setup

A Gray code lists all 8 3-bit numbers so that neighbours differ in exactly one bit. The formula is i ^ (i >> 1).

Step 1 / 10
Java
1static List<Integer> grayCode(int n) {
2 List<Integer> result = new ArrayList<>();
3 for (int i = 0; i < (1 << n); i++) {
4 result.add(i ^ (i >> 1)); // binary XOR itself shifted right
5 }
6 return result;
7}

Live state

0 codes

n

3

count

8

No condition evaluated on this step
Execution trace (dry run table)Show 10 steps
StepLineStateWhat happens
1L3n=3 count=8A Gray code lists all 8 3-bit numbers so that neighbours differ in exactly one bit. The formula is i ^ (i >> 1).
2L4i=0 gray=000i = 0 gives 0.
3L4i=1 gray=001Compared with the previous code 000, only bit 0 changed.
4L4i=2 gray=011Compared with the previous code 001, only bit 1 changed.
5L4i=3 gray=010Compared with the previous code 011, only bit 0 changed.
6L4i=4 gray=110Compared with the previous code 010, only bit 2 changed.
7L4i=5 gray=111Compared with the previous code 110, only bit 0 changed.
8L4i=6 gray=101Compared with the previous code 111, only bit 1 changed.
9L4i=7 gray=100Compared with the previous code 101, only bit 0 changed.
10L6count=8Each step flips exactly one bit, so a rotary encoder or a Karnaugh map never sees two bits change at once. The sequence is also cyclic: the last code differs from the first in one bit.
6.07.3

Bit reversal

To reverse the bits of a 32-bit int, swap adjacent bits, then adjacent pairs, then nibbles, then bytes, then halves. Each step uses a mask and a shift, and the whole thing takes 5 steps instead of 32. Integer.reverse does exactly this.

Bit reversal is the permutation at the start of the fast Fourier transform and appears in problems that ask to reverse the binary form of a number.

Java
1static int reverseBits(int x) {
2 x = ((x & 0x55555555) << 1) | ((x >>> 1) & 0x55555555); // swap adjacent bits
3 x = ((x & 0x33333333) << 2) | ((x >>> 2) & 0x33333333); // swap adjacent pairs
4 x = ((x & 0x0F0F0F0F) << 4) | ((x >>> 4) & 0x0F0F0F0F); // swap nibbles
5 x = ((x & 0x00FF00FF) << 8) | ((x >>> 8) & 0x00FF00FF); // swap bytes
6 return (x << 16) | (x >>> 16); // swap halves
7}
8// same as Integer.reverse(x)
6.07.4

Carry and overflow

Addition can be built from bit operations. The XOR of two numbers is their sum without carries, and (a & b) << 1 is the carries. Add them again, and repeat until no carry remains. This works for negative numbers too, because of two's complement.

To detect int overflow of a + b without using long, check whether both operands have the same sign and the result has a different one: ((a ^ r) & (b ^ r)) < 0 where r = a + b. Java also provides Math.addExact, which throws an ArithmeticException.

Java
1static int add(int a, int b) {
2 while (b != 0) {
3 int carry = (a & b) << 1; // positions where both bits are 1 carry to the next one
4 a ^= b; // sum without carries
5 b = carry;
6 }
7 return a;
8}
9
10static boolean addOverflows(int a, int b) {
11 int r = a + b;
12 return ((a ^ r) & (b ^ r)) < 0; // operands share a sign that the result lost
13}
6.07.5

Binary exponentiation

To compute a^b, look at the binary form of b. Each set bit contributes one of the powers a, a², a⁴, a⁸, …. Square the base every step, multiply it into the result when the current bit of b is 1, and shift b right. The loop runs once per bit of b, so it needs O(log b) multiplications instead of b - 1.

In the animation, 3^13 with 13 = 1101₂ multiplies in 3, 3⁴ and 3⁸: 3 × 81 × 6561 = 1,594,323. With a modulus applied at every multiplication this is how modular powers are computed in cryptography and combinatorics.

Java
1static long power(long a, int b) {
2 long result = 1;
3 while (b > 0) {
4 if ((b & 1) == 1) result *= a; // this bit of b is set
5 a *= a; // a, a^2, a^4, a^8, ...
6 b >>= 1;
7 }
8 return result;
9}
10
11static long powMod(long a, long b, long m) {
12 long result = 1;
13 a %= m;
14 for (; b > 0; b >>= 1, a = a * a % m) {
15 if ((b & 1) == 1) result = result * a % m;
16 }
17 return result; // m < 2^31 keeps a * a inside a long
18}

Worst

O(log b)

Space

O(1)

Binary exponentiation

Interactive · edit the input

Your input

Try

b = 13

13
12
01
10

a

3

result

1
Setup

3^13: write 13 in binary (1101). Each set bit contributes one power 3^1, 3^2, 3^4, 3^8, and the others are skipped.

Step 1 / 17
Java
1static long power(long a, int b) {
2 long result = 1;
3 while (b > 0) {
4 if ((b & 1) == 1) result *= a; // this bit of b is set
5 a *= a; // a, a^2, a^4, a^8, ...
6 b >>= 1; // move to the next bit
7 }
8 return result;
9}

Live state

0 multiplications

a

3

b

13

result

1

No condition evaluated on this step
Execution trace (dry run table)Show 17 steps
StepLineStateWhat happens
1L2a=3 b=13 result=13^13: write 13 in binary (1101). Each set bit contributes one power 3^1, 3^2, 3^4, 3^8, and the others are skipped.
2L4a=3 b=13 result=1The lowest bit of b is 1, so multiply the current power of a into the result.
3L4a=3 b=13 result=3result = 3.
4L5a=9 b=13 result=3Square the base: a = 9. It is now the power for the next bit.
5L6a=9 b=6 result=3b >>= 1 drops the bit we just used. b = 6.
6L4a=9 b=6 result=3The lowest bit of b is 0, so skip this power.
7L5a=81 b=6 result=3Square the base: a = 81. It is now the power for the next bit.
8L6a=81 b=3 result=3b >>= 1 drops the bit we just used. b = 3.
9L4a=81 b=3 result=3The lowest bit of b is 1, so multiply the current power of a into the result.
10L4a=81 b=3 result=243result = 243.
11L5a=6561 b=3 result=243Square the base: a = 6561. It is now the power for the next bit.
12L6a=6561 b=1 result=243b >>= 1 drops the bit we just used. b = 1.
13L4a=6561 b=1 result=243The lowest bit of b is 1, so multiply the current power of a into the result.
14L4a=6561 b=1 result=1594323result = 1594323.
15L5a=43046721 b=1 result=1594323Square the base: a = 43046721. It is now the power for the next bit.
16L6a=43046721 b=0 result=1594323b >>= 1 drops the bit we just used. b = 0.
17L8return=15943233^13 = 1594323 using 7 multiplications instead of 12. The loop runs once per bit of b, so O(log b).
6.07.6

Bitwise trie introduction

A bitwise trie stores numbers as paths of bits from the most significant to the least. Each node has two children, for 0 and 1. It answers questions about XOR: to find the stored number that gives the largest XOR with x, walk down and at each bit prefer the child with the opposite bit.

The maximum XOR of any two numbers in an array of n values takes O(n · 32) with this trie, much better than the O(n²) of trying all pairs. Tries are covered fully in Chapter 28.

Java
1static int maxXorPair(int[] nums) {
2 int[][] child = new int[nums.length * 31 + 2][2]; // child[node][bit], 0 means none
3 int nodes = 1;
4 int best = 0;
5 for (int x : nums) {
6 int cur = 0, xorHere = 0, probe = 0;
7 for (int bit = 30; bit >= 0; bit--) {
8 int b = (x >> bit) & 1;
9 // insert x
10 if (child[cur][b] == 0) child[cur][b] = nodes++;
11 cur = child[cur][b];
12 }
13 probe = 0;
14 for (int bit = 30; bit >= 0; bit--) {
15 int b = (x >> bit) & 1;
16 if (child[probe][1 - b] != 0) { xorHere |= 1 << bit; probe = child[probe][1 - b]; }
17 else probe = child[probe][b];
18 }
19 best = Math.max(best, xorHere);
20 }
21 return best;
22}

Practice ladder

Now try it yourself

Easy

  • Parity of a 32-bit integer
  • Convert a Gray code back to binary
  • Reverse the bits of an 8-bit value

Medium

  • Add two integers without + or -
  • Pow(x, n) in O(log n) including negative exponents
  • Generate the n-bit Gray code sequence

Advanced

  • Maximum XOR of two numbers in an array
  • Count pairs with XOR less than k using a bitwise trie
  • Modular inverse by exponentiation (Fermat) for a prime modulus

Key takeaways

  • Parity folds with XOR in log w steps; Gray code is i ^ (i >> 1).
  • Bit reversal and addition are built from masks, shifts, XOR and carries.
  • Exponentiation by squaring reads the bits of the exponent: O(log b); a trie over bits answers maximum-XOR queries.