Two's Complement, Explained: How One Bit Signs Your Integers
A plain-English walk through two's complement: how the high bit signs a number, why subtraction becomes addition, how to negate by hand, and how overflow works.
Two's Complement, Explained: How One Bit Signs Your Integers
The first time I had to read a negative number out of a hex dump, I stared at 0xFB for a solid minute trying to figure out where the minus sign went. There isn't one. A signed byte has no separate place to write "this is negative" the way decimal does. Instead, the sign is baked into the bits themselves, and the rule that does the baking is called two's complement. Once it clicks, almost every "why is my int wrong" mystery in low-level code stops being a mystery.
This post is the explanation I wish I'd had then: how the top bit signs the number, how to flip a value's sign by hand, why this representation lets a CPU subtract by adding, and how overflow and bit widths bite you. If you want to skip the arithmetic and just type numbers, the Two's Complement Converter does the three-step derivation for you at 4, 8, 16, 32, and 64 bits.
The High Bit Carries the Sign
Two's complement has no meaning until you fix a bit width. That single fact trips up more people than anything else. The same eight digits 11111111 are 255 when you read them as an unsigned magnitude and -1 when you read them as a signed 8-bit value. Neither reading is wrong; they answer different questions.
Here is what the width buys you. In an n-bit signed integer, the leftmost bit is the sign bit. If it is 0, the number is zero or positive and you read the remaining bits as ordinary binary. If it is 1, the number is negative. So 01111111 (top bit 0) is 127, and 10000000 (top bit 1) is -128. The sign bit is not a flag stapled on the side; it is a full participant in the value, and that is exactly what makes the arithmetic work out.
The values you can store run from -2^(n-1) up to 2^(n-1)-1. For 8 bits that is -128 to 127. For 16 bits, -32768 to 32767. Notice the asymmetry: the negative side reaches one further than the positive side, because zero claims a slot on the positive end.
To Negate a Number, Invert Every Bit and Add One
The mechanical rule is short: to flip a number's sign, invert all bits and add one. That intermediate "invert all bits" step has its own name, one's complement, and on its own it is not the answer. You have to add one.
Walk through it with a small value. Start from 1, which in 8 bits is 00000001. Invert every bit to get 11111110. Add one and you land on 11111111, which is -1. The top bit is set, so the converter reads it as negative, and the magnitude works out to exactly one. That is why -1 is "all ones" in any width: 16-bit -1 is sixteen ones, 32-bit -1 is thirty-two ones, and so on.
The single most common hand-conversion mistake is stopping at the one's complement and forgetting the add-one. Flip the bits of 5 (00000101) and you get 11111010, which is -6, not -5. The plus-one is what separates a clean representation from an off-by-one bug.
A Worked Example: Representing −5 in 8 Bits
Let me do -5 in 8-bit two's complement end to end, the same three rows a textbook grades and the same three rows the converter prints.
- Write the magnitude.
5in 8 bits is00000101. This is the sign-magnitude form. - Take the one's complement. Flip every bit:
00000101becomes11111010. - Add one.
11111010 + 1 = 11111011.
So -5 is 11111011, which is 0xFB in hex. The top bit is 1, confirming it is negative. If you ever want to sanity-check the result, negate it back: invert 11111011 to 00000100, add one to get 00000101, which is 5. Negation is its own inverse, exactly as it should be.
This reversibility is not a coincidence. It is the property that makes two's complement worth using, and it leads straight into the next section.
Why Subtraction Becomes One Operation
Here is the payoff that justifies the whole scheme. In two's complement, a - b is just a + (-b), and -b is "invert and add one." So a processor does not need a subtractor at all. It feeds a, the inverted bits of b, and a carry-in of 1 into the very same adder it uses for addition. One circuit handles both.
Try 7 - 5 in 8 bits. That is 7 + (-5), which is 00000111 + 11111011. Add them and you get 1 00000010. The carry off the top falls off the edge of an 8-bit register, leaving 00000010, which is 2. Correct, and no special-case logic was needed.
Compare this with the older sign-magnitude scheme, where the top bit is purely a sign flag. There, addition has to inspect the signs, decide whether it is really an addition or a subtraction, and handle the awkward fact that you get two zeros, +0 and -0. One's complement has the same double-zero problem. Two's complement's add-one step is precisely what collapses -0 back into a single 0 and lets one ordinary adder serve signed and unsigned numbers alike. That is why nearly every modern CPU stores signed integers this way.
Overflow, Bit Width, and Where It Bites
Because the range is fixed, a value can simply not fit, and what happens then depends on whether the tooling warns you. The signed 8-bit range tops out at 127 and bottoms out at -128. Ask for -129 in 8 bits and there is no honest bit pattern to give you. A good converter flags it out of range rather than silently wrapping; the converter above refuses to fake a result that does not fit, which is the behavior you want when you are sizing a register.
This is where bit width stops being academic. Suppose you are laying out a struct and a temperature reading can drop to -40. Does int8 hold it? Yes, -40 is 11011000, comfortably inside -128 to 127. But push to a worst case of -200 and int8 overflows; you need int16. Catching that while you design the struct costs five seconds. Catching it at runtime, on the coldest day in production when a sensor finally reports -200, costs a great deal more.
Reading values back has the same trap in reverse. A debugger shows a 16-bit field as 0xFF80. Read it as unsigned and you get 65408 and start chasing a bug that does not exist. Pin it to 16-bit signed and it is -128, the number the hardware actually holds. Always fix the width first, then decide signed or unsigned.
Wrapping Up
Two's complement is one rule applied consistently: the top bit signs the value, and you flip a sign by inverting every bit and adding one. From that single rule you get a representation with exactly one zero, an arithmetic where subtraction is just addition, and a range that is fixed by the bit width and slightly asymmetric. Get those three ideas and the hex dumps stop lying to you.
When you want to check work or read a raw value, the Two's Complement Converter shows all three derivation rows and flags anything out of range. And if you are juggling other radixes in the same session, for instance turning that 0xFB into decimal or binary directly, the Base Converter handles the plain unsigned conversions alongside it.
Made by Toolora · Updated 2026-06-13