Negative Numbers: Two's Complement
A -bit register already wraps around mod , so the natural way to store is as the pattern for . That way one adder handles signed and unsigned numbers alike, and subtracting is just adding the negation, which you get by flipping every bit and adding 1.
What the symbols mean
- Word size
The number of bits: 8, 16, 32 or 64.
- NOT x
x with every bit flipped.
What the symbols mean
- Sign bit
The top bit, worth −2ᵏ⁻¹ instead of +2ᵏ⁻¹.
Explanation
Two's complement is simply the way computers store negative numbers: is stored as whatever you'd have to add to to roll a -bit counter over to all zeros. That's where the name comes from: the pattern for is the complement of with respect to a power of two, , meaning the two add up to exactly . It's handy because the same adding circuit then works for positive and negative numbers alike. The formula looks dense, but it's three small pieces: (one more than the biggest number bits can hold), (flip every bit of ) and (add one), with meaning the count wraps around like a clock. It's an odd-looking trick with a simple idea behind it; bear with it.
The number wheel. So far every bit pattern has meant a number 0 or bigger. What about negatives? Picture the sixteen 4-bit patterns around a clock face: 0000, 0001, …, 1111, and then back to 0000. Adding 1 steps clockwise and subtracting 1 steps back, so one step back from 0000 lands on 1111. It makes sense to call that . Two's complement reads the half of the wheel whose top bit is 1 as negative: 1000 through 1111 stand for through .
The sign bit's weight. Equivalently, the top bit of a -bit number is worth instead of : . So bits hold to , and a signed byte holds to .
Negating: flip and add 1. Here's why that recipe works. Add to its flipped version and every column holds one 0 and one 1, so there are no carries and you get . Move the 1 over: , which is exactly the pattern for on the wheel. For example , flipped is , plus one is .
A common mistake. It's tempting to think is just with the top bit switched on, , because the top bit is "the sign bit." It isn't: . Turning on the top bit adds , it doesn't attach a minus sign. To negate, flip every bit and add 1.
It's modular arithmetic. The wheel is clock arithmetic, and that's no accident. A -bit adder throws away the carry out of the top bit, so it adds mod . Two's complement just names the residues instead of , and congruences don't care which names you use. So the same adder works for signed and unsigned numbers, and subtraction is : the ALU inverts and sets the carry-in to 1. No separate subtractor needed.
Overflow. When the true answer leaves the range, it wraps around the wheel: in 8 bits, . The tell is that adding two numbers with the same sign gives a result with the opposite sign. C defines unsigned arithmetic as arithmetic mod , but overflowing a signed integer is undefined behavior, so careful firmware checks before it adds.
uint8_t a = 255;
a = a + 1; // 0: unsigned math wraps mod 256
int8_t b = -5; // stored as 1111 1011 (0xFB)
int16_t c = b; // sign-extended to 0xFFFB, still -5
Sign extension. To widen a signed number, copy its sign bit into the new high bits: () becomes , still . Why does that keep the value? The top five 1s are worth , exactly what the old sign bit was worth on its own.
The 2038 problem. Unix systems count time as seconds since the start of 1970, traditionally in a signed 32-bit integer. It runs out at seconds, at 03:14:07 UTC on 19 January 2038. One second later the bits wrap to , which reads as December 1901. Systems are moving to 64-bit time, which lasts about 292 billion years.
See it
Worked examples
Find the 8-bit two's complement pattern for .
- 1
.
Compute in 4-bit two's complement.
- 1
and .
Practice
Type your answer and check it. Solve them all to complete the lesson.
What signed value does the 8-bit two's complement pattern hold?
Write as an 8-bit two's complement pattern, in hex with the prefix 0x.
What are the smallest and largest values of a signed 16-bit integer? Enter both, smallest first.
In 8-bit signed arithmetic, what does give?
A drone's barometer reports its altitude relative to the takeoff point as a signed 16-bit integer in centimeters, sent as two bytes, high byte first. The firmware receives 0xFF and then 0x38. What altitude does that encode, in centimeters?
Unsigned -bit arithmetic is arithmetic mod , and the C standard defines it that way. What does the 8-bit unsigned multiplication give?
Sign-extend the 4-bit pattern to 8 bits. Give the new pattern in hex, with the prefix 0x.
The hardest one here. Get it, and you've got this lesson.
In 8-bit two's complement, which values satisfy ? Enter all of them.
Where it's used
Every integer type in C, from int8_t to int64_t, is two's complement (C23 makes it official). That's why adding 1 to the largest int wraps to the most negative one, why sensor drivers have to sign-extend raw readings, and why the 2038 problem exists.