Why Two's Complement works

About to read computer science, I have just stumbled accross the concept of "Two's complement". I understand how to apply the "algorithm" to calculate these on paper, but I have not yet obtained an understanding of why it works. I think this site: https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html provides an explanaition why "flipping the digits" and adding one produces the compliment. What I do not understand is why adding the complement is equivalent to substracting the original number. Could somebody please give an explanation (maybe with a decimal example of the same concept as well?)?

Many thanks!


Solution 1:

I'll stick to 8-bit quantities, but the same applies in general.

The key to understanding two's complement is to note that we have a set of finitely many (in particular, $2^8$) values in which there is a sensible notion of addition by $1$ that allows us to cycle through all of the numbers. In particular, we have a system of modular arithmetic, in this case modulo $2^8 = 256$.


Intuitively, arithmetic modulo $n$ is a system of addition (and subtraction) in which overflow and underflow cause you to "cycle back" to a value from $0$ to $n-1$. A classic example is the usual "clock arithmetic", which is to say arithmetic modulo $12$.

For example, if it is $11\!:\!00$ now, then three hours later it will be $2\!:\!00$, since $$ 11 + 3 = 14 \equiv 2 \pmod {12} $$ and similarly, if it is $1\!:\!00$, then $4$ hours ago it was $9$ since $$ 1 - 4 = -3 \equiv 9 \pmod{12} $$ Notice that subtracting $4$ hours on the clock is the same as adding $12 - 4 = 8$ hours. In particular, we could have computed the above as follows: $$ 1 - 4 \equiv 1 + 8 = 9 \pmod{12} $$ That is: when performing arithmetic modulo $n$, we can subtract $x$ by adding $n-x$.


Now, let's apply this idea modulo $256$. How do you subtract $3$? Well, by the above logic, this is the same as adding $256 - 3 = 253$. In binary notation, we could say that subtracting $00000011$ is the same as adding $$ 1\overbrace{00000000}^8 - 00000011 = 1 + \overbrace{11111111}^8 - 00000011 = 11111101 $$ and there's your two's complement: the calculation $(11111111 - 00000011)$ "flips the bits" of $00000011$, and we add $1$ to this result.


Note 1: In the context of arithmetic with signed integers, we don't think of $11111101$ as being $253$ in our $8$-bit system, we instead consider it to represent the number $-3$. Rather than having our numbers go from $0$ to $255$ around a clock, we have them go from $-128$ to $127$, where $-x$ occupies the same spot that $n - x$ would occupy for values of $x$ from $1$ to $128$.

Succinctly, this amounts to saying that a number with 8 binary digits is deemed negative if and only if its leading digit (its "most significant" digit) is a $1$. For this reason, the leading digit is referred to as the "sign bit" in this context.

Note 2: An interesting infinite analog to the two's complement system of subtraction is that of infinite series 2-adic numbers. In particular, we can say something strange like $$ \dots 11111 = -1 $$ since $\dots 11111$ is the "infinite two's complement" of $1$.

Solution 2:

Let's look at a decimal example. You want to do $735-78$.

Borrow 1000 from the Number Bank; the loan is subject to no interest, but you must give back what you got as soon as you have used it.

Now consider that $$ 735-78=735+(1000-78)-1000 $$ The subtraction $1000-78$ is very easy to do: just do $9$-complement on the rightmost three digits (the missing one at the far left is, of course, $0$), getting $921+1$, so our operation now reads $$ 735-78=735+921+1-1000 $$ Since \begin{array}{rr} 735 & + \\ 921 & = \\ \hline 1656 \end{array} we can give back 1000 to the bank and add 1: $$ 735-78=656+1=657 $$

In base two it's exactly the same, with the only difference that $1$-complement (instead of $9$-complement) is very easy, because it consists in flipping the digits. You don't need the loan either, because you work on a fixed number of bits, and numbers that overflow are simply reduced forgetting the leftmost digit. So if you have to do

00101001 - 00001110

you can flip the digits in the second number and add, forgetting the leftmost bit that may become 1:

00101001 +
11110001 =
----------
00011010 +
       1 =
----------
00011011

Solution 3:

The way we represent it is ambiguous. If you are explicit about all unstated bits, then simply flipping all bits negates the number. But you need to have a representation with a decimal point and bits stating what all unstated bits are. For example, say that we are explicit about what unstated bits are so that we can combine signed, unsigned, and uncarried numbers explicitly:

This is zero: 0..0000.0000..0

Use "..1" to represent that there are no zeroes on the right: 0..0000.1111..1

If you perform the carry, you get zeroes on the right and it becomes 1. This is a mechanical definition that can be checked and done in an actual (finite) machine. Then -1 is:

1..1111.0000..0

Add 1 + -1:

0..0000.1111..1 + 1.1111.0000..0

That gives you: 1..1111.1111..1, which has a carry to be done due to the "..1". So after carry, it's 0..0000.0000..0.

So now we negate an integer. It has all zero bits on the right. So it triggers a carry, which is the same as adding 1.

0..010.000..0

1..101.111..1

1..110.000..0

So the fact that you "flip the bits and add 1" is a little bit of an illusion created by being ambiguous about what all the bits are. It is an emergent optimization for finite integer representations to state it that way.

Negate 1/2:

0..000.100..0

1..111.011..1

1..111.100..0

That's -1 + 1/2.