A memory cell only knows 0 and 1. There is no physical minus sign. So how does a computer store a number like ? The answer is two’s complement, the encoding that virtually every modern CPU (and every C compiler we care about) uses for signed integers. This note explains it from scratch. You only need Binary Number System as background.

The Problem

With bits we have patterns. If we only ever stored plain binary (called unsigned), every pattern would mean a value from to , and negative numbers would be impossible.

Idea: sacrifice the leftmost bit (the MSB) to act as a sign bit, and design a rule so that the remaining bits encode the magnitude of negative values in a clever way.

One’s Complement

The one’s complement of a binary number is obtained by flipping every bit: every 0 becomes 1 and every 1 becomes 0:

Legend: what each symbol means

SymbolMeaning
Some number, written as an -bit pattern
The pattern with every bit flipped. This bar is the standard math symbol for “the flip of”. So is the one’s complement
The all-ones pattern ( ones in a row)
The value of all-ones, from the “all ones trick” in Binary Number System > Counting and the All Ones Trick
”which means” / “therefore”: the right part follows from the left part

Why the equation is true, in words: a bit and its flip always add up to 1 (0+1 = 1, 1+0 = 1). So column by column, plus its flip gives 1 in every position, the all-ones pattern. Rearranging that fact (“take away from all-ones”) gives the right-hand side: the flip of equals .

Check it with numbers

, : flipping the bits should give the same answer as subtracting:

And indeed 0001 1001 flipped bit-by-bit is 1110 0110. ✓ Both roads lead to the same pattern.

One's complement of 25

 +25:   0001 1001
 flip:  1110 0110   ← one's complement

Historically, “negative ” was stored as the one’s complement of . But this has an ugly flaw: it produces two zeros.

 +0:   0000 0000
 -0:   1111 1111   ← same value, different pattern!

Two patterns for zero wastes one pattern, forces hardware to special-case zero, and makes arithmetic awkward. This is why nobody uses it in practice. Instead we take one more step.

Two’s Complement

The two’s complement of is its one’s complement plus one:

Legend: what each symbol means

SymbolMeaning
The -bit pattern we use to represent the negative of (not a new pattern, a reinterpretation)
“is the same thing as”
The subtraction: subtract from the total pattern count
“modulo ”: if the result needs more than bits, throw the extra bits away. With only bits there are just patterns, so anything past wraps around, like a clock: 13 o’clock is 1 o’clock

In words: to negate , compute . The wrap-around rule is what makes this legal. The pattern for and the pattern we call are the same -bit object.

Check it with numbers

, :

Same pattern as the flip-and-add-1 recipe below. Subtracting from and “flip then add 1” are two routes to the same destination.

Recipe to negate a number

  1. Write the number in binary (positive, plain binary).
  2. Flip every bit → one’s complement.
  3. Add 1 → two’s complement.

Worked example: store in one byte

Step 1: magnitude in binary

Step 2: one’s complement (flip all bits)

 0001 1001  →  1110 0110

Step 3: add 1

   1110 0110
 +         1
 -----------
   1110 0111   ← two's complement of 25

So is stored as 1110 0111, which in hex is 0xE7.

The recipe also works in reverse. Flipping the bits of 1110 0111 and adding 1 gives back 0001 1001 (). Negation is its own inverse.

Reading a stored byte back

Given a pattern, how do we know which number it represents?

  • MSB = 0: the number is non-negative. Just read it as plain binary.
  • MSB = 1: the number is negative. Its value is .

In one formula, for an -bit pattern :

Legend: what each symbol means

SymbolMeaning
The signed value the pattern represents
The MSB (leftmost bit), the sign bit
The MSB’s column, counted as negative. If the sign bit is 0 this term is ; if it’s 1 it contributes
Every other column, the ordinary “bit times its weight” from Binary Number System > Key Formulas, all positive
”add up all those ordinary columns”, from position 0 to (everything except the sign bit)

In words: read the pattern exactly like plain binary, except the leftmost column has a minus sign in front of it.

Reading 1110 0111 back, column by column

Same answer as the shortcut “MSB is 1, so subtract ”: . Exactly the we stored.

Why Computers Use Two’s Complement

  1. Only one zero. 0000 0000 is the only zero, so no pattern is wasted.
  2. Subtraction becomes addition. can be computed as using the same adder circuit the CPU already has, so no separate subtractor is needed.
  3. No exceptions. Adding works identically whether the operands are positive or negative (any carry out of the MSB is simply discarded).
   25:  0001 1001      (0x19)
 + -25:  1110 0111      (0xE7)
  -----------------  ┐
        1 0000 0000   ┘ discard the 9th bit
   =      0000 0000   = 0 ✓

Extremes Worth Memorizing

Working these out with the recipe makes the signed range intuitive:

ValuePattern (byte)HexWhy
1111 11110xFFAll magnitude bits set, the “last” step before wrapping to 0
1000 00000x80Sign bit set, all magnitude bits zero, the most negative byte
0111 11110x7FSign bit clear, all magnitude bits set, the largest positive byte

Legend: the pattern 1000 0000 read as plain binary is . Since its MSB is 1 we subtract (the “reading it back” rule), and . This is why all-zeros-magnitude lands at the most negative value, not at zero.

Key Formulas

OperationFormula (for bits)In plain words
Negate Subtract from the total pattern count. Any overflow past bits wraps away
One’s complementSubtract from all-ones, which is the same as flipping every bit
Value of a patternRead it like plain binary, but the leftmost column counts negative
Signed range to Half the patterns go to , the other half to
Unsigned range to All patterns used as plain binary magnitudes

Related: Char Data Type shows these formulas in action on the char data type.