Positive and Negative Two Complement Numbers
Negative Numbers: Two's complement representation
So far, we've just used positive numbers.
In decimal, we use a minus sign to indicate a negative number. But
the CPU and memory just know 1's and 0's, that is all. So how do we determine
a negative number from hexadecimal or binary representation.
-
All of the values in a computer are of some fixed length of bits. In an
HC11, most are 8 bits, and some are 16 bits. But we always know the
size of a register or memory. This is important:
values are always a fixed number of bits.
-
One idea is to take the leftmost bit, and interpret it as a sign bit. That
is, if the left bit is a 0, the number is positive, and if the left bit
is one, the number is negative.
-
8-bit example: 00001011 is positive decimal 11, and 10001011 is decimal
-11.
-
But, now we would need electronics to treat the leftmost bit as a
special bit.
-
An ingenious solution: the 2's complement representation (2C)
-
Take the positive binary number, complement (flip to opposite) all the
bits, then add one
-
Thus, decimal -11 is found by:
-
taking positive decimal 11, which is 00001011
-
complementing it, which gives us 11110100 (each bit is opposite)
-
then adding 1, which gives us 11110101
-
then, decimal -11 is 8-bit binary 11110101
-
2C still has a sign bit: a leftmost 1 means negative, a leftmost 0 means
positive
-
But arithmetic can be done completely ignoring whether a number is signed
or not.
-
That means (important): the CPU doesn't care if you are using signed
numbers or not!
Modulo Arthmetic
Here's another related problem: we only have fixed-length registers in a
computer. In the case of the hc11, the registers that we use for
arithmetic (the A register and the B
register) are only 8 bits wide. So what happens if we
add something like
a3
+b4?
The main thing is, we get a result that won't fit. The fix for this
is to always do modulo arithmetic: so any arithmetic operation using
an 8 bit register is performed mod 256. We can actually turn this
problem to our advantage, if we're clever about it. In order to fit
this example on the blackboard, let's assume 3 bit numbers, so we have
the numbers 0 through 7 and all arithmetic is mod 8 (or radix 8).
Let's make a number line. Unfortunately, since 0 has to follow 7, it
can't be a line; it has to be a circle. Now, adding goes clockwise on
the circle, and subtracting goes counterclockwise. Here's a picture of
the number circle, showing the circle, the numbers on it in decimal,
the same numbers in binary, and which direction to go for addition and
subtraction.
Question: If I go from 3 to 1, can you tell whether I did it by
subtracting 2, or by adding 6? Well, no you can't. Now, here's an
idea: any time we want to subtract some number n, we can instead add
(8-n). And that means that the way we represent a negative number
``-n'' is by using (8-n) instead.
Now, we'll let the lower half of the numbers of our circle, 0, 1, 2,
3, represent themselves, and the upper half of the numbers of our
circle, 4, 5, 6, 7, represent negative numbers, where the negative
number is the number of digits we are subtracting from 0. Therefore,
the number 7 is the negative number -1 because it is the first slot to
the left of zero, etc. That means we can tell a positive number from
a negative number by the most significant bit: we'll call this the
Sign Bit. When that bit is 0, we will call the number positive and
when the bit is 1, we will call the number negative. So now it looks
like this:
One last wrinkle: if we have to subtract and negate by taking 8-n, we
haven't bought ourselves anything: we still have to subtract. But
remember that 8-n = (7-n)+1. And 7-n can be calculated easily by just
inverting each bit in the pattern for n! We can just negate every bit
in n and that equals 7-n for all n.
Converting from Decimal to another Radix
Suppose we want to convert a negative decimal number to hexadecimal. Take
-97, and convert it to 8 bit hexadecimal, for example. Here are the
steps:
- Remember that it's negative.
- Use the division method to convert the magnitude to hexadecimal:
Old | Radix | New | Digit |
97 | 16 | 6 | 1 |
6 | 16 | 0 | 6 |
So we get 6116.
- Convert 6116 to binary by just writing out the nibbles.
- Invert the bits - replace every
1
with a
0
, and every 0
with a 1
.
Working in hexadecimal, this is equivalent to subtracting every digit
from f
. Since we're assuming a 8 bit word, we need to
subtract from ff
(you actually need to subtract from
enough f
's to fill out the word. If it were a 16 bit
word, we'd need ffff
).
ff - 61 = 9e
.
- Add 1.
9e + 1 = 9f
.
You can check your work by adding your result from step 1 to your
final result. You should get 0, with a carry-out.
Converting to Decimal from some Radix
Now suppose we have a number such as 11011110 that we want to convert
from signed eight-bit binary to decimal. This time, the steps are as
follows:
- Look at the most significant bit. Since it's a 1, we'll need to
negate the number. If it were a 0, we'd skip to step 4.
- Invert all the bits. this turns it into
00100001
.
- Add 1. That will give us
00100010
.
- Use the multiplication method to convert it to decimal:
Old |
Digit |
New |
Radix |
0 | 0 | 0 | 2 |
0 | 0 | 0 | 2 |
0 | 1 | 1 | 2 |
2 | 0 | 2 | 2 |
4 | 0 | 4 | 2 |
8 | 0 | 8 | 2 |
16 | 1 | 17 | 2 |
34 | 0 | 34 | |
- Since the number is negative, we get -34.