Code_ The Hidden Language of Computer Hardware and Software - Charles Petzold [54]
we write them this way:
500 501 502 … 996 997 998 999 000 001 002 003 004 … 497 498 499
Notice that this forms a circle of sorts. The lowest negative number (500) looks as if it continues from the highest positive number (499). And the number 999 (which is actually –1) is one less than zero. If we add 1 to 999, we'd normally get 1000. But since we're only dealing with three digits, it's actually 000.
This type of notation is called ten's complement. To convert a 3-digit negative number to ten's complement, we subtract it from 999 and add 1. In other words, the ten's complement is the nines' complement plus one. For example, to write –255 in ten's complement, subtract it from 999 to get 744 and then add 1 to get 745.
You've probably heard it said that "Subtraction is merely addition using negative numbers." To which you've probably replied, "Yeah, but you still have to subtract them." Well, using the ten's complement, you don't subtract numbers at all. Everything is addition.
Suppose you have a checking account balance of $143. You write a check for $78. That means you have to add a negative $78 to $143. In ten's complement, –78 is written as 999 –078 + 1, or 922. So, our new balance is $143 + $922, which equals (ignoring the overflow), $65. If we then write a check for $150 dollars, we have to add –150, which in ten's complement equals 850. So our previous balance of 065 plus 850 equals 915, our new balance. This is actually equivalent to –$85.
The equivalent system in binary is called two's complement. Let's assume that we're working with 8-bit numbers. These range from 00000000 to 11111111, which normally correspond to decimal numbers 0 through 255. But if you also want to express negative numbers, every 8-bit number that begins with a 1 will actually represent a negative number, as shown in the following table:
Binary
Decimal
10000000
–128
10000001
–127
10000010
–126
10000011
–125
⋮
11111101
–3
11111110
–2
11111111
–1
00000000
0
00000001
1
00000010
2
⋮
01111100
124
01111101
125
01111110
126
01111111
127
The range of numbers that you can represent is now limited to –128 through +127. The most significant (leftmost) bit is known as the sign bit. The sign bit is 1 for negative numbers and 0 for positive numbers.
To calculate the two's complement, first calculate the ones' complement and then add 1. This is equivalent to inverting all the digits and adding 1. For example, the decimal number 125 is 01111101. To express –125 in two's complement, first invert the digits of 01111101 to get 10000010, and then add 1 to get 10000011. You can verify the result using the preceding table. To go backward, do the same thing—invert all the bits and add 1.
This system gives us a way to express positive and negative numbers without using negative signs. It also lets us freely add positive and negative numbers using only the rules of addition. For example, let's add the binary equivalents of –127 and 124. Using the preceding table as a cheat sheet, this is simply
The result is equivalent to –3 in decimal.
What you need to watch out for here is overflow and underflow conditions. That's when the result of an addition is greater than 127 or less than –128. For example, suppose you add 125 to itself:
Because the high bit is set to 1, the result must be interpreted as a negative number, specifically the binary equivalent of –6. Something similar happens when –125 is added to itself:
We decided at the outset that we're restricting ourselves to 8-bit numbers, so the leftmost digit of the result must be ignored. The rightmost 8 bits are equivalent to +6.
In general, the result of an addition involving positive and negative numbers is invalid if the sign bits of the two operands are the same but the sign bit of the result is different.
Now we have two different ways of using binary numbers. Binary numbers can be either signed or unsigned. Unsigned 8-bit numbers range from 0 through 255. Signed 8-bit numbers range from –128