Two’s complement

Author
Affiliation

Clayton Cafiero

University of Vermont

Published

2025-10-13

TipNote

Mathematical terms used here: additive inverse, subtrahend, minuend, addend.

The additive inverse of a number is what we must add to the number to get zero. For every positive number, the negative number of the same magnitude is the additive inverse. For every negative number, the positive number of the same magnitude is the additive inverse. Zero is its own additive inverse.

  • -1 is the additive inverse of 1, because 1 + (-1) = 0.
  • 1 is the additive inverse of -1, because -1 + 1 = 0.
  • -42 is the additive inverse of 42, because 42 + (-42) = 0.
  • 42 is the additive inverse of -42, because -42 + 42 = 0.
  • 0 is the additive inverse of 0, because 0 + 0 = 0.

A subtrahend is a number to be subtracted from another. For any x - y, y is the subtrahend. In this example, x is the minuend—the number we’re subtracting from.

An addend is something we add. With 1 + 2 = 3, 1 and 2 are the addends and 3 is the sum.

Two’s complement representation of signed integers

When discussing binary numbers up to this point, we’ve restricted our discussion to non-negative integers, 0, 1, 2, \ldots. However, we all know we can have negative integers, and we work with them all the time. The question arises, “How do computers represent negative integers?” and the answer likely is not what you might think. When performing arithmetic or manipulating equations and terms algebraically, we prefix negative numbers with a negative sign: -5, -42, -x, -y, etc. Usually it’s understood that without this prefix, a number is not negative, but sometimes we use a + prefix when we need to disambiguate.

Is this how we handle negative numbers in a computer? No.  Not quite.

In a computer, everything is binary and any given bit pattern could be interpreted in different ways. We have signed and unsigned readings of given bit patterns. Signed representations can be interpreted as having positive or negative value, or zero, of course. Unsigned representations are interpreted as only having non-negative values—positive values or zero only.

One might naïvely think that the only difference between signed and unsigned integers would be how we read the leftmost bit—with one value we interpret as a positive number (or zero) and with a different value, we’d interpret as a negative number. Again, this is not quite how it works.

Signed integers are represented using the two’s complement method. In this representation, all negative numbers have a leading 1 and non-negative numbers do not. But it’s not as simple as just prefixing with a sign indicator.

Let’s say we have some 8-bit number (just for ease of presentation), and say we wanted to construct the additive inverse of this number. Here’s how we’d go about it: first we invert all the bits in the number (swapping zeros with ones and vice versa) and then we add one. That’s how we construct the additive inverse of any signed integer.

For example, take decimal 42, which in binary looks like this:

0001 0110

To construct -42 we invert the bits and add one.

0001 0110 (decimal 42)
1110 1001 (invert bits)
1110 1010 (add one, and now we have -42)

Keep in mind that how these are interpreted depends on the length of the bitstring. 1111 interpreted as a 4-bit two’s complement bitstring has the value -1. However, 0000 1111 interpreted as an 8-bit two’s complement bitstring has the value 15.

Why do we use this representation? Conserving bits is a benefit, but it’s not the only reason. With this representation, we don’t need two different circuits to perform addition and subtraction.

If we want to subtract, we can compute the additive inverse of the subtrahend and add. With bitwise operations computing the additive inverse is easy:

neg_s = ~s + 1    # ~ is the bitwise NOT operator

Let’s see how we can use this to perform subtraction.

Subtraction with two’s complement

We all know that adding a -x is the same as subtracting x, but how would we go about this with binary numbers using a naïve representation? Let’s try 9 - 15.

1001
-1111

Now we’re faced with a little problem: carrying is easier than borrowing. In the ones column we’d subtract 1 from 1 and get 0. In the two’s column, we’d have to borrow from the eights column, then borrow from the fours column. Now we have borrowed 10 in the two’s column, but that takes more than a single bit, so we’d need a two-bit borrow! We’d get 10 - 1 = 1 in the two’s column, leaving a 1 borrowed in the fours column. We subtract 1 - 1 and get 0 in the fours column. Now we have 0 - 1 in the eights column and nowhere to borrow from! Agh! What a mess.

I strongly encourage you to pause and try this out with pencil and paper on your own. Remember we have no digit two—we can only work with zeros and ones. You’ll see immediately how complicated this gets.

Hopefully after trying this out, you’ll see that the hardware used for our ripple-carry adder just won’t work. We’d need two different circuits, one for addition and one for subtraction, and the latter would be more complex.

Building a separate circuit would be ugly, complicated, and unnecessary.

How two’s complement fixes subtraction

Now let’s see how pretty this is with two’s complement. We’ll take the exact same example, 9 - 15 but this time we’ll use eight bits and two’s complement representation.

With eight bits, decimal 9 is 0000 1001 and decimal 15 is 0000 1111. We know that adding a negative number is the same as subtracting a positive number, so we’ll compute the additive inverse of 15 to get -15.

0000 1111 (decimal 15)
1111 0000 (invert the bits)
1111 0001 (add 1, and now we have -15)

Now let’s add 9 + (-15).

0000 1001 (addend)
1111 0001 (addend)
1111 1010 (sum)

Let’s check our work. We know that 9 - 15 = -6. If we’ve done this correctly then if we compute the additive inverse of the sum we should get 6.

1111 1010 (sum)
0000 0101 (invert the bits)
0000 0110 (add 1, and now we have 6)

So we do indeed get 6 as expected—our work checks out.

We didn’t have to carry anywhere, you say, and that’s right, so let’s try this with a problem that involves carries in computing the sum. Let’s try 127 - 110, which is the same as 127 + (-110).

1111 1100 (carry)
0111 1111 (decimal 127)
1001 0010 (decimal -110)
0001 0001 (sum)

Hey! Wait a minute! What happened to the last carry in the leftmost column? We call that overflow and we just throw it away. Now, if we did this right, that sum should be decimal 17.

1 \times 16 + 0 \times 8 + 0 \times 4 + 0 \times 2 + 1 \times 1 = 17

and again our math checks out.

Hey! Wait a minute! That has carries, but the result is still positive. OK. Let’s see one more, with carries and a negative result. We’ll do 31 - 42.

0011 1100 (carry)
0001 1111 (decimal 31)
1101 0110 (decimal -42)
1111 0101 (sum)

Let’s verify that the additive inverse of the sum is indeed 11 as we’d expect.

1111 0101 (sum)
0000 1010 (invert bits)
0000 1011 (add 1, and we get decimal 11)

Again, our math checks out.

This means that in order to do subtraction we can use the same ripple-carry adder as we used for addition of unsigned integers, with only a modification to compute the additive inverse of a subtrahend.

What’s next?

However, there’s an elegant design of an adder that allows for addition and subtraction with only a minor change (and the basic design of the ripple-carry adder is unchanged).

For this design, we need a multiplexer.

© 2025 Clayton Cafiero.

No generative AI was used in writing this material. This was written the old-fashioned way.