How do computers represent negative numbers?
And a secondary question is - and why does it matter?
The concept I am trying to explain here is the idea of “1’s complement”. Computers work in a number system called binary which only has the digits 1 and 0 but since as humans we are more used to the decimal system which uses the digits 0 through to 9. I hope this makes it easier to explain how computers represent negative numbers by explaining the concept using decimal numbers.
Let’s go back to long addition. When we add two numbers together like 19 and 24 hopefully you were fortunate to be taught the skills of how to add these together:
19
+24
---
Step 1 - add 9 +4 = 13
Since we have an overflow of 10 we write the remainder in the right most column
and carry the 1 in the next column. Think of it like:
19
+ 24
(+10) <----- Carry the 1 from adding 9 + 4
----
3
Then we add 1 + 2 which equals 3 and we add the 1 from the overflow from
the first step - so 1 + 2 + 1 = 4. Now we get the final result:
19
+ 24
----
43
Tada!
So hopefully the above makes sense. But imagine we are a computer and we can only count up to 99 because we only are allowed to store two digits. This is how computers work - although the digits are actually 0 and 1 and the number of digits is more likely to be 32 or 64 etc. When we talk about a 32 bit integer in computers we are referring to a number that consists of 32 0’s or 1’s.
Now let’s consider what happens when we add the number 99 to any number when we are not allowed to store the 100’s column.
23 → 23 + 99 = 122 but remove the hundred’s column → 22
12 → 23 + 99 = 111 but remove the hundred’s column → 11
1 → 1+ 99 → 100 → 0
2 → 2 + 99 → 1 → 1
10 → 10 + 99 → 109 → 9
What pattern can you see?
Look carefully at the input number on the left and the output number on the right:
23 → 22
12 → 11
1 → 0
2 → 1
10 → 9
What is happening?
Every time we add 99 and throw away the hundreds digit it’s equivalent to subtracting 1 from the original number.
Whoa! We’re getting close to understanding how computers represent negative numbers.
What happens if we add 95 and throw away the hundreds column:
23 → 23 + 95 = 118 → 18
12 → 12 + 95 = 107 → 7
10 → 10 + 95 = 105 → 5
What is the pattern?
Adding 95 and throwing away the hundreds digit is equivalent to subtracting 5.
How would we subtract 10 from a number? What about adding 90?
23 → 113 → 13
12 → 102 → 2
10 → 90 → 0
Yep - that works!
So by adding numbers and throwing away the top digit we can achieve subtraction. We have three examples:
99 is -1
95 is -5
90 is -10
What is the pattern?
The pattern is we take 100 and subtract the number we which to subtract. So -49 is represented by 100-49 = 51. Think of 51 as the “complement” of 49. To show that:
49 + 51 → 100 → 0
Yep! That works.
This is how computers represent negative numbers. But this also helps us understand some problems with how computers represent negative numbers. How would we represent -51?
100 - 51 = 49
So let’s try adding 49 to some numbers:
1 → 1 + 49 → 50
2 → 2 + 49 → 51
Oh crap. It is not working.
What is the problem?
The issue is that using only two digits and using this “complement” system to represent negative numbers means we have some limits as to what numbers we can express.
It means the lowest negative number we can express is -49 and the highest positive number is 50. Otherwise we run into what in computer language we call an “overflow” error. Let’s see what overflow errors look like. Imagine we have the number 0 and we subtract 26 from it twice using complement numbers. So the complement of 26 is 74. So:
0 + 74 = 74 - this is fine since 74 is really -26 using the complement system
74 + 74 = 148 → 48 - oh no! We 48 - this is an overflow error!
We end up with the number 48 which means the positive number 48 in our number system - we have what in computer language we call an overflow error.
Overflow errors can cause minor and major problems with computer software.
Why is that?
Well consider if you have a number represented in 8 bits. So 8 1 or 0 digits which means that the maximum number is 2 to the power 8 minus 1. i.e. 2x2x2x2x 2x2x2x2 = 256 take away 1 which is
2^8-1 = 255
i.e a number from 0 to 255 if we treat this as an unsigned number or if we treat is as signed then we have the range from -127 to 127.
In binary 127 looks like this:
0111 1111
In binary 255 looks like this:
1111 1111
But as we know from the complement discussion we can also think of 255 as -1.
We have a choice in computer software to think of 1111 1111 as 255 or as -1 just depending on whether we are choosing to treat the number as ‘signed’ i.e. -126 to 127 or as ‘unsigned’ - meaning 0 to 255.
So what happens when we have an unsigned number with the value 0 and we subtract 1 from it?
Well:
0000 0000 The value 0
+ 1111 1111 The value -1 in one's complement
___________
1111 1111 This means 255 if we think of as an unsigned integer or -1
if it represents a negative number
So subtracting 1 from an unsigned integer with the value 0 results in the largest number which is possible to represent in that number. i.e. 255 in this case.
We would see the same thing with unsigned integers with larger sizes like 16 bits or 32 bits ( 16 or 32 digits respectively). i.e.
8 bits - largest number 2^8 -1 = 255. So 0 minus 1 = 255
16 bits - largest number 2^16 -1 = 65535. So 0 minus 1 = 65535
32 bits - largest number 2^32-1 = 4294967295. So 0 minus 1 = 4294967295
And so now you can see some of the problems that representing numbers in unsigned integers in software can cause. Software developers without experience (myself included) can make the mistake that it makes sense to represent things which should only be positive using the unsigned integers but if a problem occurs such that the count is off by one negatively all of sudden we see a variable become a huge unexpected number.
These numbers can be just a cosmetic problem - like on our logging system in Iguana 6.1 showing large numbers. But can create big problems like a software program hanging if the software has a loop which iterates from 0 to 4294967295 when the software developer was assuming that the variable was only likely to be a small positive number.