The Bitwise Operators
The Bitwise Operators
The previous page established that an integer is a fixed row of bits. This page is the instruction set
for editing that row. There are six operators — & (AND), | (OR),
^ (XOR), ~ (NOT), << (left shift), >>
(right shift) — plus one Java-only variant, >>> (unsigned right shift). Each is
a single CPU instruction acting on a whole word at once. Master exactly what each does to one bit, and what
it does to a word follows by applying that rule to every column in parallel.
The bit-logic three: AND, OR, XOR (and NOT)
& | ^ are binary and defined bit-by-bit; ~ is unary and just flips. The
whole truth of them fits in one table:
| a | b | a & b | a | b | a ^ b | ~a | |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 | |
| 0 | 1 | 0 | 1 | 1 | 1 | |
| 1 | 0 | 0 | 1 | 1 | 0 | |
| 1 | 1 | 1 | 1 | 0 | 0 |
Read them as intentions, not just tables — this is how you choose the right operator:
- AND = "keep only where both are 1". Its job is masking:
x & maskextracts the bits ofxthatmaskselects and zeros the rest. - OR = "1 if either is 1". Its job is setting bits on:
x | maskforces every bit ofmaskto 1 inx, leaving the others untouched. - XOR = "1 if they differ". Its job is toggling:
x ^ maskflips exactly the bitsmaskselects. Its difference-detecting nature is why it cancels pairs — the whole next page is built on it. - NOT = flip all. Because flipping gives
(2ⁿ-1) - x, it obeys~x = -x - 1in two's complement — e.g.~12 = -13.
Apply them to a whole word by lining the operands up in columns. With a = 12 and
b = 10 (four bits shown):
| bit3 (8) | bit2 (4) | bit1 (2) | bit0 (1) | = value | |
|---|---|---|---|---|---|
| a = 12 | 1 | 1 | 0 | 0 | 12 |
| b = 10 | 1 | 0 | 1 | 0 | 10 |
a & b | 1 | 0 | 0 | 0 | 8 |
a | b | 1 | 1 | 1 | 0 | 14 |
a ^ b | 0 | 1 | 1 | 0 | 6 |
Each output column applies the truth table to the two bits above it — nothing more. That column
independence is exactly why the operation is O(1): no column depends on another, so hardware
does all 32 at once.
Shifts: sliding the whole row
A shift moves every bit left or right by k positions. Because place values are powers of
two, sliding left one place doubles each place value:
- Left shift
x << kmoves bits toward the MSB and feeds0s in at the bottom. Numerically it isx × 2ᵇ(as long as nothing falls off the top).12 << 1 = 24:1100→1 1000. - Right shift
x >> kmoves bits toward the LSB, dropping what falls off the bottom — roughlyx ÷ 2ᵇ.12 >> 1 = 6:1100→0110. The question is what fills the top.
Arithmetic vs logical right shift — and Java's >> vs >>>
When shifting right, the vacated top bits must be filled with something. There are two policies:
- Arithmetic (sign-preserving): copy the sign bit into the top. This keeps the number's sign and
makes
>>equal to floor division by 2ᵇ for negatives too. - Logical (zero-fill): always shove
0s in at the top, treating the value as a raw unsigned bit pattern.
Java gives you both operators explicitly: >> is arithmetic,
>>> is logical (unsigned). Watch the difference on -8, whose 32-bit
pattern is 1111…1000:
| expression | top fill | result bits (32) | value |
|---|---|---|---|
-8 >> 1 | copy sign (1) | 1111…1100 | -4 |
-8 >>> 1 | zero | 0111…1100 | 2147483644 |
The logical shift produces (2³² - 8) / 2 = 4294967288 / 2 = 2147483644 — it
read -8 as the large unsigned value 4294967288 and halved that.
>>> is the tool when a number is really a 32-bit pattern (a hash, a bitset, a
color) rather than a signed quantity.
Go's approach: the type decides, no >>>
Go deliberately has no >>>. Instead the operand's type chooses the fill:
shifting a signed integer is arithmetic; shifting an unsigned integer
(uint32, uint, …) is logical. To get Java's >>> you
convert to an unsigned type first: uint32(int32(n)) >> 1 reinterprets the bits as
unsigned and zero-fills — giving the same 2147483644. Go also adds one operator Java
lacks: &^ (AND NOT, "bit clear"), where a &^ b clears in a
exactly the bits set in b — 12 &^ 10 = 4 (it clears bit 1, which is
set in 10). It is shorthand for the very common a & ~b.
Shifting as ×2 / ÷2 — the traps
Treating shifts as fast multiply/divide is fine if you respect the fixed width and the sign. Three traps bite people constantly:
- Left-shift overflow.
1 << 31is not two billion — the bit lands on the sign position, so in a 32-bitintit is-2147483648. Shifting a positive number past bit 30 can flip it negative. If you need the value, use a 64-bit type or an unsigned type. - Right shift of a negative is floor, not truncation.
-7 >> 1 = -4(arithmetic shift rounds toward −∞), whereas integer division-7 / 2 = -3(rounds toward zero). So>>and/ 2are not interchangeable for negatives — a subtle source of off-by-one bugs. - Out-of-range shift counts differ by language. In Java the count is masked to the low bits of the
width, so
1 << 32 == 1(32 & 31 = 0) and1 << 33 == 2— a notorious surprise. In Go, a run-time shift count ≥ the type's width yields0(shiftingint32(1)by a variable holding 32 gives 0) — and a constant over-shift likeint32(1) << 32is a compile error, not a wrap. Never shift by a count you have not bounded to0..width-1.
Java: every operator, one run
public class Operators {
public static void main(String[] args) {
int a = 12, b = 10; // 1100, 1010
System.out.println(a & b); // 8 -> 1000
System.out.println(a | b); // 14 -> 1110
System.out.println(a ^ b); // 6 -> 0110
System.out.println(~a); // -13 (~x == -x - 1)
System.out.println(a << 1); // 24 (x * 2)
System.out.println(a >> 1); // 6 (x / 2)
int n = -8; // ...11111000
System.out.println(n >> 1); // -4 arithmetic: sign copied
System.out.println(n >>> 1); // 2147483644 logical: zero-filled
System.out.println(-7 >> 1); // -4 (floor toward -inf)
System.out.println(-7 / 2); // -3 (truncate toward 0) -- NOT the same
System.out.println(1 << 31); // -2147483648 (bit lands on sign)
}
}Go: the same operators, plus &^
package main
import "fmt"
func main() {
a, b := 12, 10 // 1100, 1010
fmt.Println(a & b) // 8
fmt.Println(a | b) // 14
fmt.Println(a ^ b) // 6
fmt.Println(^a) // -13 (unary ^ is NOT in Go)
fmt.Println(a << 1) // 24
fmt.Println(a >> 1) // 6
n := -8
fmt.Println(n >> 1) // -4 signed -> arithmetic
fmt.Println(uint32(int32(n)) >> 1) // 2147483644 unsigned -> logical
fmt.Println(a &^ b) // 4 AND NOT: clear a's bits that are set in b
fmt.Println(-7 >> 1) // -4 (floor, like Java)
var one int32 = 1
fmt.Println(one << 31) // -2147483648 (bit lands on sign)
}
Note the two Go-specific facts this run exercises: Go's right shift picks arithmetic vs logical purely
from the operand's signedness (hence the uint32 conversion to reproduce Java's
>>>), and &^ gives a one-token bit-clear that Java writes as
a & ~b.
Takeaways
&masks (keep),|sets (turn on),^toggles (flip on difference),~inverts — each defined per bit, applied to every column in parallel.- Right shift needs a fill policy: arithmetic copies the sign (floor ÷ 2ᵇ),
logical zero-fills. Java exposes both as
>>/>>>; Go picks by operand type, so convert touint32for a logical shift. - Shifts are ×2 / ÷2 only within the fixed width and with the sign in mind:
1<<31goes negative,-7>>1 = -4 ≠ -7/2 = -3, and out-of-range counts wrap in Java but zero out in Go.
Operator reference for the Bit Manipulation module. Every truth-table value, word-level result, and cross-language shift behavior above was executed on JDK 8 and Go 1.25 and matched digit for digit.
🤖 Don't fully get this? Learn it with Claude
Stuck on The Bitwise Operators? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **The Bitwise Operators** (DSA) and want to truly understand it. Explain The Bitwise Operators from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **The Bitwise Operators** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **The Bitwise Operators** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **The Bitwise Operators** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.