HomeDigital ElectronicsTwo's Complement
SIGNED BINARY · ALU HARDWARE

Two's Complement — and How an Adder Subtracts

By EcrioniX · 14 Sep 2026 · ~16 min read

RISC-V, ARM and x86 all store signed integers in two's complement. That is not a fashion. It is the encoding that lets one n-bit adder compute both A + B and A − B with almost no extra delay. This page is the public, school-to-interview version: why not ones' complement, and why there is no separate “subtractor arithmetic” in a typical ALU.

Names, so we do not argue in the comments

Industry says 2's complement and 1's complement. Pedants write two's complement (complement with respect to 2n) and ones' complement (complement of ones). Same circuits. We use both.

1. Unsigned bits cannot mean “minus”

Four bits can name 16 patterns. If you treat them as unsigned, they mean 0 through 15. To also mean negative integers you must reuse some of those patterns. The encoding is a contract: everyone (compiler, ALU, human) agrees what 1111 means.

Three historical contracts:

2. Ones' complement: flip the bits

To form −N, invert every bit of N. In 4-bit, 3 is 0011, so −3 is 1100.

The trap is zero. Inverting 0000 gives 1111. Both are “zero”: +0 and −0. Comparators, beqz-style branches, and “is this register empty?” now have two answers for nothing.

Addition is also messy. If A + B produces a carry-out of 1, you must add that 1 back into the sum (end-around carry). That is a second add on the critical path — or extra muxing around the same adder.

Worked 1's add: 5 + (−3)

0101 + 1100 = 1_0001. The leading 1 is Cout. End-around: 0001 + 1 = 0010 = +2. Correct answer, extra step. That extra step is why 1's lost in ALUs. (Some early machines did use it. Modern ISAs did not keep it for integers.)

3. Two's complement: flip, then add 1

−N = bitwise NOT of N, then +1. 4-bit 3: ~0011 = 1100, plus 1 = 1101. That pattern is −3.

Why +1? Mathematically you want N + (−N) = 2n, which looks like 0000 plus a carry out of the top bit. Ones' complement sums to 1111 (all ones = −0), not a clean wrap. The extra +1 slides the negative half so there is only one zero.

Decimal2's complement (4-bit)1's complement (4-bit)
+701110111
+501010101
+100010001
000000000 (+0) and 1111 (−0)
−111111110
−311011100
−710011000
−81000no code (range is −7…+7)

n-bit two's complement range is −2n−1 … +2n−1−1. Four bits: −8 to +7. The extra negative is real; 1000 has no positive partner. That is not a bug — it is the leftover code-point after you refuse to spend one pattern on a second zero.

4. Why 2's won

Ones' complement is not “wrong maths.” It is worse silicon for a general-purpose integer ALU. That is the honest sentence.

5. There is no subtractor — you add

A standard-cell library may contain adder macros. The ALU does not need a second arithmetic block that “does minus.” For two's complement:

The identity

A − B = A + (~B) + 1
Invert B bit-wise. Put 1 on the adder’s carry-in. The leftover carry-out of the n-bit word is dropped (modulo 2n), which is exactly wraparound two's arithmetic.

One control bit named sub can do both jobs: XOR every bit of B with sub (so sub=1 inverts), and drive cin from the same sub.

ModeB pathcinResult
ADDB unchanged0A + B
SUBbitwise invert B1A + (~B) + 1 = A − B
n-bit adder used as subtractor A B XOR B ⊕ sub n-bit ADD cin = sub sum, cout Result sub
The SUB instruction does not buy a new ALU. The decoder asserts sub.

Paper check: 7 − 3

A = 0111, B = 0011. Invert B → 1100. cin = 1.

0111 + 1100 + 1 = 1_0100. Keep the low four bits: 0100 = 4. The leading 1 is the wrap carry; you do not add it back in (that would be ones' complement).

If you invert B and leave cin = 0, you compute A + ~B = A − B − 1 (here: 3). That is the classic student bug: you built a ones'-style invert and forgot the two's +1.

Same adder, 5 + (−3)

−3 is already 1101 in 4-bit two's. 0101 + 1101 = 1_00100010 = +2. No extra end-around. Compare to the 1's example above: same answer, one less add.

6. Verilog: synthesis infers this

Writing a - b is fine. The synthesizer builds invert + cin. If you instantiate only an adder, you still subtract by feeding it ~b and cin=1. Compact form:

addsub.v — n-bit add / subtract
module addsub #(parameter N = 4) (
  input  wire [N-1:0] a,
  input  wire [N-1:0] b,
  input  wire         sub,   // 0 = add, 1 = subtract
  output wire [N-1:0] y,
  output wire         cout
);
  // sub=1 → XOR inverts b, and cin is 1  →  y = a + (~b) + 1 = a - b
  assign {cout, y} = a + (b ^ {N{sub}}) + sub;
endmodule

This is combinational teaching RTL. A real CPU ALU still uses this idea; it may pipeline, flag overflow separately, and share the adder with address calculation. The identity does not change.

7. Do not confuse Cout with signed overflow

Unsigned add: Cout = 1 means the true sum did not fit in n bits.

Unsigned subtract: Cout is often treated as a borrow (polarity depends on how you defined cin; be consistent with your textbook).

Signed two's overflow is different: two positives added to a negative result, or two negatives to a positive. Hardware: carry into the MSB XOR carry out of the MSB. Wiring overflow = cout for signed subtract is a common interview fail.

What to remember

FAQ

Why two's complement instead of ones' complement?

One zero, and add/sub share one adder with no end-around carry. Ones' has +0/−0 and a second add of Cout.

How does an adder act as a subtractor?

A − B = A + (~B) + 1. Invert B; set cin to 1. The SUB opcode asserts that control. There is no second “minus ALU.”

How do I form two's complement by hand?

Invert bits, add 1. Shortcut: copy bits from the right through the first 1, then invert the rest.

Why does 4-bit two's go to −8 but not +8?

16 patterns. After pairing +1/−1 … +7/−7 and a single 0, one pattern remains: 1000 = −8.

Is a − b in Verilog a different circuit from an adder?

No. Synthesis infers invert-B plus cin. You can write it explicitly as a + (~b) + 1.

Keep learning