How two's complement works
Computers have no minus sign, only bits. Two's complement is the convention almost every CPU uses to store negative integers: the top bit (the sign bit) counts as negative 2ⁿ⁻¹ instead of positive. In 8 bits, the top bit is worth −128, so 1111 1111 = −128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = −1, and 1000 0000 is −128.
The same bits read as unsigned are 255 and 128. Whether a value is “really” negative is decided by the type in your code (int8_t versus uint8_t), not by the bits. That is why this tool always shows both readings.
Negating a number by hand
- Write the positive value in binary at the full width (42 =
0010 1010). - Invert every bit (
1101 0101). - Add 1 (
1101 0110). That is −42, which is 0xD6 or 214 unsigned.
The trick works in both directions: applying it to −42 gives 42 back. The tool above prints these steps for whatever you enter.
Why it is designed this way
With two's complement the hardware does not need a separate subtractor. Adding a negative number is ordinary binary addition with the carry out of the top bit thrown away: 42 + (−42) = 0010 1010 + 1101 0110 = 1 0000 0000, drop the ninth bit, get 0. There is also only one zero, unlike sign-magnitude or one's complement.
Ranges
| Width | Signed range | Unsigned range | Typical type |
|---|---|---|---|
| 8 | −128 … 127 | 0 … 255 | int8_t, byte |
| 16 | −32,768 … 32,767 | 0 … 65,535 | int16_t, short |
| 32 | −2,147,483,648 … 2,147,483,647 | 0 … 4,294,967,295 | int32_t, int |
| 64 | −9.22 × 10¹⁸ … 9.22 × 10¹⁸ | 0 … 1.84 × 10¹⁹ | int64_t, long |
The negative range is one larger than the positive, because zero takes one of the “positive” patterns. That is why -INT_MIN overflows: 128 does not fit in a signed byte.
Questions
What is −1 in two's complement?
All ones: 0xFF in 8 bits, 0xFFFF in 16, 0xFFFFFFFF in 32. Inverting 0000 0001 gives 1111 1110, adding 1 gives 1111 1111.
How do I tell if a binary number is negative?
Look at the leftmost bit at the declared width. If it is 1 and the value is signed, the number is negative. 1000 0000 is −128 as int8 but 128 as uint8; the bits alone do not decide.
Why does 127 + 1 give −128?
Signed overflow. 0111 1111 + 1 = 1000 0000, which sets the sign bit. In C this is undefined behaviour; in most other languages the value wraps around to −128.
What is sign extension?
Widening a signed value by copying the sign bit into the new top bits, so −1 (0xFF) becomes 0xFFFF, not 0x00FF. Casting an int8 to int16 does this automatically; casting an unsigned does zero extension instead. Switch widths above with a negative number entered to see it.
Is the sign bit just a flag?
No. In two's complement the sign bit has a value (−2ⁿ⁻¹) and takes part in the arithmetic. That differs from sign-magnitude, where the top bit only marks the sign, and from floating point, where the sign is a separate flag.