home comics writing pictures archive about

2024-03-16 - DataTypes: Negative Numbers

DataTypes

In the last two posts we've been looking at positive binary numbers but what about negative binary numbers? How do we represent those? The simple answer is that you put a negative sign in front of the number, so –101 would be –5, but that just changes the question to how do we record a negative sign with bits?

Well, we have a few options.

Sign and magnitude

The simplest way is to use one of the bits to indicate the sign. This is typically the most significant bit. So if you were using an 8-bit byte to store a number, bit 7 would encode the sign and bits 0-6 would encode the value. This means you can encode the values –127 to +127. The sign bit is usually 0 to indicate positive and 1 for negative but it's only really important that the machines using these numbers are consistent about the meaning.

The main problem with this approach is that it requires computers to analyze the bits before performing operations.

5 + –7?

We are adding a negative value so that can be converted to subtraction.

5 – 7

The right number is bigger than the left number so we know the value will be negative and the value will be the same as if the subtraction was done the other way around.

-(7 – 5) = –2 = 5 + – 7

5 + – 3?

We are still adding a negative value so that can be converted to subtraction.

5 – 3

This time the right number is smaller than the left so we know the value will be positive and we can just do the subtraction.

5 – 3 = 2

Another oddity with sign and magnitude is that you have both –0 and +0 as we can have a zero magnitude with either sign.

This isn't terribly complicated and, as we will see later, this is how standard floating-point numbers are encoded. It would be nice to speed up the process if we can, especially when computers are going to be doing these kinds of calculations billions of times a second.

Ones' complement

Another option is Ones' complement which defines negative numbers as being the binary complement of the positive number with the same value. Binary complement means to flip all of the bits, 0 –> 1 and 1 –> 0. So for –56 we take 0011 1000 (56 in binary) and flip all the bits to get 1100 0111. The most significant bit still acts as a sign bit as it will only be set for negative numbers but now it's also a part of the value. The range of values is still –127 to +127 and we still have two zeros (0000 0000 and 1111 1111) but we have to do less special work to handle negative numbers in calculations. We add and subtract numbers as normal but if we get a carry or a borrow left over then we have to apply that back to the result.

For example if we were using 4-bit numbers we could do 5 + -7 which would be 0101 + 1000 (The complement of 0111 which is 7).

0101+ 1000=1101

We know the result is negative because the most significant bit is set and if we take the complement we get 0010 which is 2. So 5 + –7 = –2 as we expect.

What if we try 5 + –3? That would be 0101 + 1100.

11 0101+ 1100=0001

Our current result is 0001 which is 1. 5 + –3 = 1? That doesn't seem right. We have a carry left over though, so with ones' complement we need to add that back to the result.

1 0001+ 0001=0010

Which gives us 0010 or 2. 5 + –3 = 2 makes a lot more sense.

So Ones' complement simplifies performing math with negative numbers at the cost of making negative numbers a little harder to decode. Can we do better?

Two's complement

The answer is yes. Two's complement defines negative numbers as being the complement of the positive number plus one. This is sort of like pre-including that extra addition step. The easiest way to determine the Two's complement of a value is to switch every value after the first 1. So for –56 we start with 0011 1000, keep all of the bits up to the one in the fourth spot the same and then flip all the bits after that to get 1100 1000.

You can also think of this as simply making the most significant bit negative. So you can determine the decimal equivalent of 1100 1000 by calculating –27 + 26 + 23 = –128 +  64 + 8 = –56. Like Ones' complement we don't have to handle the sign separately but now we also don't need to perform a second calculation.

For 5 + –7 we do 0101 + 1001

1 0101+ 1001=1110

Again the most significant bit is 1 indicating a negative number. If we take the two's complement of 1110 we get 0010 which is 2, so the result of 5 + –7 is –2.

And for 5 + –3 we do 0101 + 1101

11 1 0101+ 1101=0010

We ignore the carry out in this case and get 0010 which is the correct result of 2.

We can also think of negative numbers in two's complement as being the positive value subtracted from 0. To determine –9 we can calculate 0 – 9 or 0000 – 1001.

1 1 1 100000- 1001=0111

Which gives us our expected result of 0111. To do this calculation we had to borrow from some kind of nether space but that's okay because we got the result that we wanted.

With an 8-bit Two's complement number the range of values – 128 – 127. So we have an extra value now. Another benefit of Two's complement is we only have one zero because the two's complement of 0000 0000 is 0000 0000. So that's nice.

Two's complement is by far the most common way to represent negative numbers because we don't need to do any extra steps to get the correct result.

Next time we will start to look at fractional numbers

Comments: