Negative Numbers in Binary - Two's Complement and Why It's Clever
Helpful context:
Binary as introduced so far only handles non-negative integers. But real programs subtract, go into debt, measure temperatures below zero. The question is not whether computers represent negative numbers - obviously they do - but how, and why the particular method chosen is so much cleverer than the obvious alternatives.
There are three natural approaches. Two of them have subtle bugs. The third is the one every modern processor uses.
The Obvious Idea: Sign-Magnitude
The simplest idea: dedicate the leftmost bit as a sign flag. 0 means positive, 1 means negative. The remaining bits hold the magnitude as usual.
In 8-bit sign-magnitude:
- $+5$ is $0\ 0000101$
- $-5$ is $1\ 0000101$
Problem 1: Two zeros. $0\ 0000000$ is $+0$ and $1\ 0000000$ is $-0$. These represent the same value but have different bit patterns. Any comparison with zero requires checking both patterns. Software and hardware both get more complicated.
Problem 2: Addition breaks. Try $5 + (-3)$ hoping for $2$:
$0\ 0000101 + 1\ 0000011 = 1\ 1000000$ which sign-magnitude reads as $-64$. Wrong. Signed addition requires completely different circuitry from unsigned addition. You cannot reuse your adder hardware.
Better: One’s Complement
One’s complement negates a number by flipping every bit. $+5 = 0000\ 0101$, so $-5 = 1111\ 1010$.
Addition works better now, but there is a catch - when there is a carry out of the MSB, you must add it back to the result (the “end-around carry”).
$5 + (-3)$: $0000\ 0101 + 1111\ 1100 = 1\ 0000\ 0001$. The carry out gives $0000\ 0001$, add it back: $0000\ 0010 = 2$. Correct, but requires special-casing the carry.
One’s complement still has two zeros: $0000\ 0000 = +0$ and $1111\ 1111 = -0$. The two-zeros problem persists.
The Right Idea: Two’s Complement
Two’s complement negates a number by flipping all bits and then adding 1.
$+5 = 0000\ 0101$ Flip: $1111\ 1010$ Add 1: $1111\ 1011 = -5$ in two’s complement
Only one zero. $0000\ 0000$ flipped and incremented: $1111\ 1111 + 1 = 1\ 0000\ 0000$. The carry out is discarded (we only have 8 bits), leaving $0000\ 0000$. Zero is unique.
Addition requires no special cases. Try $5 + (-3)$:
$$0000\ 0101 + 1111\ 1101 = 1\ 0000\ 0010$$
Discard the carry out, read the result: $0000\ 0010 = 2$. Correct - and the carry was discarded automatically by the fixed bit-width. No end-around carry, no sign logic. The same adder circuit works for all integers.
Why does it work? In 8-bit arithmetic, $256 \equiv 0$. Two’s complement represents $-x$ as $256 - x$. So $5 + (-3) = 5 + (256 - 3) = 258 = 256 + 2 \equiv 2 \pmod{256}$. The “negative” numbers are just large positive numbers that, when added to positive numbers, produce results in the right range. The hardware does not know or care about signs - it just adds and discards overflow.
Reading Two’s Complement
The MSB still acts as a sign indicator: 0 means non-negative, 1 means negative. But the magnitude is not simply the remaining bits.
Positive numbers (MSB = 0): read normally.
Negative numbers (MSB = 1): to find the magnitude, flip all bits and add 1.
$1111\ 1011$: flip to get $0000\ 0100$, add 1: $0000\ 0101 = 5$. So $1111\ 1011 = -5$.
Alternatively: the value of an $n$-bit two’s complement number with bits $b_{n-1} \ldots b_1 b_0$ is:
$$-b_{n-1} \cdot 2^{n-1} + b_{n-2} \cdot 2^{n-2} + \cdots + b_0 \cdot 2^0$$
The MSB has a negative weight. Everything else works as unsigned. $1111\ 1011$: $-128 + 64 + 32 + 16 + 8 + 0 + 2 + 1 = -128 + 123 = -5$. ✓
Range and Overflow
For $n$ bits, two’s complement represents:
$$-2^{n-1} \text{ to } 2^{n-1} - 1$$
For 8 bits: $-128$ to $127$. The range is asymmetric because there is one zero (not two), and the negative side gets the extra slot: $1000\ 0000 = -128$ has no positive counterpart (trying to negate it gives $-128$ again).
| Bits | Unsigned range | Two’s complement range |
|---|---|---|
| 8 | 0 to 255 | -128 to 127 |
| 16 | 0 to 65,535 | -32,768 to 32,767 |
| 32 | 0 to ~4.3 billion | ~-2.1 billion to ~2.1 billion |
| 64 | 0 to ~18.4 × $10^{18}$ | ~-9.2 × $10^{18}$ to ~9.2 × $10^{18}$ |
Overflow happens when a result exceeds this range. Adding $127 + 1$ in 8-bit two’s complement gives $-128$ - the value wraps around. This is not a hardware error; it is the defined behavior of fixed-width arithmetic. C and many languages leave signed overflow undefined precisely because detecting it requires extra hardware or instructions.
Subtraction for Free
Two’s complement makes subtraction identical to addition: to compute $a - b$, compute $a + (-b)$ where $-b$ is the two’s complement of $b$.
This is enormously valuable. It means a processor needs only one addition circuit to handle both addition and subtraction. No separate subtraction logic. The ALU (Arithmetic Logic Unit - the part of the processor responsible for all arithmetic and bitwise operations) computes $a + \text{NOT}(b) + 1$ (NOT flips all bits; adding 1 completes the two’s complement negation) with a single unified path.
Discomfort check. If two’s complement is just modular arithmetic, how does the CPU know whether $1111\ 1011$ is $-5$ or $251$? It does not. The bit pattern is the same either way. The interpretation - signed or unsigned - is a matter of which instruction you use.
ADDandADDS(signed add) on ARM,imulvsmulon x86: these produce identical bit patterns but set different overflow flags and are used in different contexts. The hardware manipulates bits; your code decides what they mean.
Summary
| Representation | Negation | Two zeros? | Hardware addition |
|---|---|---|---|
| Sign-magnitude | Flip MSB | Yes | Requires separate logic |
| One’s complement | Flip all bits | Yes | Needs end-around carry |
| Two’s complement | Flip all bits + 1 | No | Standard addition, discard carry |
Two’s complement wins because it requires no special cases - the same adder handles all arithmetic, and zero has a unique representation. Every modern CPU uses it.
Read next: