Bit Manipulation Basics
Bit manipulation means working directly on the binary digits of a number with operators such as AND, OR, XOR and shifts. It turns some problems that look like they need extra memory or nested loops into a single pass with a few variables, and it is how sets, flags and permissions are stored compactly in real systems.
This page covers the operators, how Java stores integers, the handful of tricks that appear again and again, and which problem uses which trick.
Binary and Two's Complement
A Java intlongk131101
Negative numbers use two's complement: -xx0000 01011111 10101111 1011int
is all ones:-1 .1111…1111- The range is uneven: -2,147,483,648 to 2,147,483,647.
is still negative.Math.abs(Integer.MIN_VALUE) - Addition and subtraction work the same way for negative and positive numbers, which is why the hardware uses this format.
Java has no unsigned intintInteger.toUnsignedStringInteger.toBinaryString
The Operators
| Operator | Name | Result bit is 1 when… | Example (4 bits) |
|---|---|---|---|
& | AND | both bits are 1 | 1100 & 1010 = 1000 |
| | OR | either bit is 1 | 1100 | 1010 = 1110 |
^ | XOR | the bits differ | 1100 ^ 1010 = 0110 |
~ | NOT | the bit was 0 | ~1100 = 0011 |
<< k | left shift | moves bits up by k, fills with 0; multiplies by 2k | 0011 << 2 = 1100 |
>> k | signed right shift | moves bits down, copies the sign bit in | -8 >> 1 = -4 |
>>> k | unsigned right shift | moves bits down, fills with 0 | -8 >>> 1 = 2147483644 |
Two Java pitfalls cause real bugs:
- Shift distances wrap. For an
, Java takes the shift distance modulo 32, soint is the same as1 << 32 , which is 1, not 0. For a1 << 0 it is modulo 64. Uselong when1L << k can reach 32 or more.k - Precedence.
,& and^ bind more loosely than| , so== does not compile. Writex & 1 == 0 .(x & 1) == 0
XOR: The Most Useful Operator
Most of the clever solutions on this site rely on four properties of XOR:
: equal values cancel.x ^ x = 0 : zero is neutral.x ^ 0 = x- Order does not matter: XOR is commutative and associative.
- It is its own inverse: if
, thena ^ b = c .a ^ c = b
So XORing a list in which values appear in pairs leaves only the unpaired ones, whatever the order. That is the idea behind
Common Tricks
| Goal | Expression | How it works |
|---|---|---|
| Is bit k set? | (x >> k) & 1 | moves bit k to position 0 and keeps only it |
| Set bit k | x | (1 << k) | OR with a mask that has only bit k |
| Clear bit k | x & ~(1 << k) | AND with a mask that has every bit except k |
| Toggle bit k | x ^ (1 << k) | XOR with 1 flips a bit |
| Clear the lowest 1 bit | x & (x - 1) | 1100 & 1011 = 1000 |
| Keep only the lowest 1 bit | x & -x | 11000100 |
| Is x a power of two? | x > 0 && (x & (x - 1)) == 0 | a power of two has exactly one 1 bit |
| Is x even? | (x & 1) == 0 | the lowest bit is the remainder mod 2 |
The JDK also has these built in, and the JIT compiler turns many of them into single CPU instructions: Integer.bitCountInteger.numberOfTrailingZerosInteger.numberOfLeadingZerosInteger.highestOneBitInteger.lowestOneBitInteger.reverseInteger.rotateLeftLong
public class BitTricks {
public static void main(String[] args) {
int x = 0b1011_0100; // 180
System.out.println((x >> 2) & 1); // 1 is bit 2 set?
System.out.println(x | (1 << 0)); // 181 set bit 0
System.out.println(x & ~(1 << 2)); // 176 clear bit 2
System.out.println(x ^ (1 << 7)); // 52 toggle bit 7
System.out.println(x & -x); // 4 lowest set bit
System.out.println(x & (x - 1)); // 176 clear the lowest set bit
System.out.println(Integer.bitCount(x)); // 4 number of 1 bits
System.out.println(Integer.numberOfTrailingZeros(x)); // 2 index of the lowest set bit
System.out.println(-8 >> 1); // -4 sign bit copied in
System.out.println(-8 >>> 1); // 2147483644 zero copied in
System.out.println(1 << 32); // 1 shift distance is taken mod 32
System.out.println(1L << 32); // 4294967296
byte b = (byte) 0xF0;
System.out.println(b); // -16 bytes are signed
System.out.println(b & 0xFF); // 240 read it as unsigned
}
}
Bitmasks as Small Sets
An intlong
- union:
; intersection:a | b ; difference:a & b ;a & ~b - "no item in common":
;(a & b) == 0 - size:
.Integer.bitCount(a)
mask(1 << n) - 1nEnumSetBitSet
Problems by Technique
| Technique | Problems |
|---|---|
| XOR cancels pairs | |
| Counting bits | |
| Per-bit reasoning | |
x & (x - 1)x & -x | |
| Shifts | |
| Arithmetic from bits | |
| Greedy, highest bit first | |
| Bitmasks as sets | |
| XOR with 1 to flip a bit | |
| Prefix XOR |