Bitwise operations
Java has six bitwise operators: AND, OR, XOR, NOT and three shifts. Each works on all bits of a value at once. Seeing them applied bit by bit makes them predictable, including the difference between the two right shifts.
Learning goals
- Predict the result of &, |, ^, ~ for any pair of ints
- Explain what << and >> do arithmetically
- Tell >> (arithmetic) from >>> (logical) for negative numbers
- Write correct expressions given Java's operator precedence
Prerequisites
Bitwise operators let you work on 32 or 64 yes/no flags in one instruction. They are the basis of masks, hash functions, compression and fast set operations on small universes.
AND
a & b has a 1 in each position where both a and b have a 1. ANDing with a mask keeps the bits that are 1 in the mask and clears the rest, which is how you extract bits.
Use the widget to try every operator. Pick an operator, enter a and b, and the result is built one bit column at a time. The display shows 8 bits, but Java computes on 32 bits; the low 8 bits are identical. The same widget covers OR, XOR, NOT and the shifts below.
| a | b | a & b | a | b | a ^ b |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
Bitwise operators
Interactive · edit the inputa = 12
b = 10
Two's complement, 8-bit view of a = 12 and b = 10. Java computes on 32-bit ints, but the low 8 bits behave identically.
1int and = a & b; // 1 only where both bits are 12int or = a | b; // 1 where at least one bit is 13int xor = a ^ b; // 1 where the bits differ4int not = ~a; // flip every bit5int shl = a << b; // shift left, fill with 06int shr = a >> b; // arithmetic shift right: copy the sign bit7int ushr = a >>> b; // logical shift right: fill with 0
Live state
a
12
b
10
Execution trace (dry run table)Show 10 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | a=12 b=10 | Two's complement, 8-bit view of a = 12 and b = 10. Java computes on 32-bit ints, but the low 8 bits behave identically. |
| 2 | L1 | bit=7 a=0 b=0 result=0 | Bit 7: 0 & 0 = 0. At least one is 0. |
| 3 | L1 | bit=6 a=0 b=0 result=0 | Bit 6: 0 & 0 = 0. At least one is 0. |
| 4 | L1 | bit=5 a=0 b=0 result=0 | Bit 5: 0 & 0 = 0. At least one is 0. |
| 5 | L1 | bit=4 a=0 b=0 result=0 | Bit 4: 0 & 0 = 0. At least one is 0. |
| 6 | L1 | bit=3 a=1 b=1 result=1 | Bit 3: 1 & 1 = 1. Both are 1. |
| 7 | L1 | bit=2 a=1 b=0 result=0 | Bit 2: 1 & 0 = 0. At least one is 0. |
| 8 | L1 | bit=1 a=0 b=1 result=0 | Bit 1: 0 & 1 = 0. At least one is 0. |
| 9 | L1 | bit=0 a=0 b=0 result=0 | Bit 0: 0 & 0 = 0. At least one is 0. |
| 10 | L1 | result=8 | 12 & 10 = 8. |
OR
a | b has a 1 wherever at least one of a and b has a 1. ORing with a mask sets those bits without touching the others. It is the standard way to combine flags: READ | WRITE.
Example: 12 | 10 = 1100 | 1010 = 1110 = 14. Select the OR operator in the widget above and check the table.
1final int READ = 1, WRITE = 2, EXEC = 4;2int permissions = READ | WRITE; // 33boolean canWrite = (permissions & WRITE) != 0; // true4permissions |= EXEC; // add a flag: 75permissions &= ~WRITE; // remove a flag: 5
XOR
a ^ b has a 1 wherever the bits differ. XOR with a mask flips the bits where the mask has a 1. Its key properties are a ^ a = 0, a ^ 0 = a, and that it is commutative and associative. Together these mean XOR undoes itself: (a ^ b) ^ b = a.
Example: 12 ^ 10 = 1100 ^ 1010 = 0110 = 6. XOR is the key to the tricks in lesson 6.05.
NOT
~a flips every bit, so ~a == -a - 1. For example ~5 = -6 and ~0 = -1. Because it flips all 32 bits of an int, the result is almost always negative for small non-negative numbers.
Use it to build a mask that clears bits: x & ~mask keeps every bit of x except those set in the mask.
1int x = 5; // 0000 01012int y = ~x; // 1111 1010 = -63int clearLow3 = x & ~7; // clears the low three bits (7 = 0b111)
Left shift
a << k moves every bit k places toward the high end and fills the vacated low bits with 0. Mathematically it multiplies by 2^k, as long as the result does not overflow. Select a << b in the widget and step through the shifts: bits that move past the top are lost.
The shift distance is taken modulo the width: for an int it is k & 31, for a long k & 63. So 1 << 32 is 1, not 0. Use 1L << k for k up to 63.
1int a = 3 << 2; // 12 (3 * 4)2int big = 1 << 31; // -2147483648 (sets the sign bit)3int wrap = 1 << 32; // 1, because 32 & 31 == 04long ok = 1L << 40;
Arithmetic right shift
a >> k moves bits toward the low end and fills the vacated high bits with a copy of the sign bit. For negative numbers the new bits are 1s, so the result is still negative. It divides by 2^k, rounding toward negative infinity: -5 >> 1 is -3, not -2.
Try -16 >> 2 in the widget: the top two positions fill with 1s and the result is -4.
1System.out.println( 16 >> 2); // 42System.out.println(-16 >> 2); // -43System.out.println( -5 >> 1); // -3 (floor of -2.5)4System.out.println( -5 / 2); // -2 (division truncates toward zero)
Logical right shift
a >>> k also moves bits toward the low end, but always fills the high bits with 0. It treats the value as unsigned. For non-negative numbers it equals >>. For negative ints it produces a large positive number: -1 >>> 28 is 15 (the top four bits of an all-ones int are cleared).
The widget's >>> works on the 8-bit view of the value, so -16 >>> 2 gives 60 there; in Java on a 32-bit int it gives 1073741820. Use >>> when you treat the bits as a bit pattern rather than a number, for example when extracting fields or reading hash bits.
1System.out.println(-1 >>> 28); // 152System.out.println(-16 >>> 2); // 10737418203int hashBits = (h >>> 16) & 0xFFFF; // extract the upper half of a hash4int mid = (lo + hi) >>> 1; // safe midpoint even if lo + hi overflows into the sign bit
Operator precedence
Bitwise operators have lower precedence than comparison operators, which catches many people out. In order from highest to lowest: + -, then shifts << >> >>>, then < <= > >=, then == !=, then &, then ^, then |, then &&, then ||.
So x & 1 == 0 is parsed as x & (1 == 0), which does not compile for an int. Always write (x & 1) == 0. When in doubt, add parentheses.
| Expression | Parsed as | Fix |
|---|---|---|
| x & 1 == 0 | x & (1 == 0) (type error) | (x & 1) == 0 |
| a + b << 2 | (a + b) << 2 | a + (b << 2) if you meant that |
| flags | READ == READ | flags | (READ == READ) (type error) | (flags | READ) == READ |
Practice ladder
Now try it yourself
Easy
- Compute 25 & 13, 25 | 13 and 25 ^ 13 by hand
- What is ~0 and what is ~(-1)?
- Evaluate -20 >> 2 and -20 >>> 28
Medium
- Swap the upper and lower halves of a 16-bit value
- Check whether two ints have opposite signs using XOR
- Write a flags class with add, remove and has
Advanced
- Compute the average of two ints without overflow using &, ^ and >>
- Implement multiplication using only shifts and adds
- Explain precisely what 1 << -1 evaluates to in Java
Key takeaways
- & keeps, | sets, ^ flips, ~ inverts; shifts multiply and divide by powers of two.
- >> copies the sign bit, >>> fills with zeros; the shift distance is taken modulo 32 or 64.
- Bitwise operators have low precedence: always parenthesise (x & mask) == value.