Signed integers represent values that are non-negative (including zero) and those that are negative, such as . In our normal notation, the unary minus operator () is used to denote negative. In a system where everything is 0 or 1, however, there is no sign to indicate whether a numbe is negative or not.
There are alternatives to use 0 and 1 to represent the sign of a value. For example, we can allocate a single bit so that if this bit is 1, the number is negative, if this bit is 0, the number is non-negative. However, such a method renders the adder and subtractor logic useless.
In order to be able to better utilize transistors in a processor, we need a representation of negative integer values so that the logic gate implementation of the adder and subtractor can be used regardless of whether a value of a representation is signed or not.
Let us consider combinations of four bits. There are only of them. If we only need to represent non-negative values, we can use all 16 combinations, from to to represent the integers from 0 to 15.
If we need to represent integer values that are both negative and non-negative, however, we will need to assign negative values to some of the these bit patterns. The only question is how to do this assignment. The objective to reuse the adder and subtractor logic helps us choose the assignment.
What we need to do is to consider the bit pattern of . Because represents zero (this one is obvious!), and represents 1, it makes sense that we can choose the result of to represent the value of . The same applies to to represent and so on.
In fact, it makes sense to use to represent the arithmetic negation of the value represented by (where each letter represents a bit). For notation purposes, we use to denote the value represented by the bit pattern .
How do we perform ? Well, the same way as do we any binary subtraction! Due to the first number being zero (), however, we have some quick tricks to get this done.
Because we are only interested in the least significant four bits of the subtraction, and would yield exactly the same result as far as the least significant four bits are concerned. The only difference is whether there is a leftover borrow from bit 4.
is . So now we have the following:
As it turns out, is a special subtraction, as well. Because we are always subtracting a zero or a one (represented by to ) from a constant of , this is the same as boolean negation. In short, , and so on. The boolean negation is applied to each bit of . In C/C++, this is represented by the bit-wise not operator (~).
Now we can finish our equation:
Of course, this rather weird way of looking at arithmetic negation is only applicable when we limit our view to a fixed number of bits. In our examples, the width of operations is 4-bit. The principle, on the other hand, applies to integers represented by any number of bits.
is called two’s complement because bitwise negation is called one’s complement.
Two’s complement does not automatically suggest the range of signed values to be represented by bits. All it says is the bitwise operation is equivalent to arithmatic negation.
However, can be used to help us here. Unless , . Because is equivalent to arithmatic negation, we can also say that unless , .
We can plug in individual 4-bit numbers to test this requirement. , but that’s okay because it is zero. , the requirement is met. This works all way until we get ! This means we have hit a limit due to the limited number of bits (4 in this case).
Then the question is what should the bit pattern represent? We have to choices, it can be or . The correct choice is as a signed number.
The superficial reason is that we already have 0 as a non-negative number, so the range of non-negative values only go from 0 to 7 for 8 of them. As a result, the negative values go from -1 to -8 also to have 8 of them.
The more rigorous reason is that we treat the most significant bit as the “sign” bit. All the negative values are represented by bit patterns with the most significant bit being a one. Given numbers are represented in -bit patterns, bit is the sign bit.
In general, given bits, the range of integer values is from to .
A comparison is, essentially, a subtraction where the difference is not important. Assuming bit patterns represent unsigned values, the borrow flag can be used to indicate ordering of values.
Assume that a binary subtractor performs the n-bit subtration . The borrow of bit is 1 iff . This makes sense because a borrow of one is triggered only if a subtraction tries to subtract more than there is available.
Can we actually prove that “a subtraction borrows from bit only if the bit 0 to bit of the minuend is less than bit 0 to bit of the subtrahend?”
As a matter of fact, we can! Again, the proof itself is CISP440 material, but included here for the curious and those desire rigorous mathematical proofs!
This is done by proof-by-induction. Let us use to denote the minuend, to denote the subtrahend. is bit of , and is bit of . We also we the notation to specify the bit pattern from bit 0 to bit of .
The base case is when we subtract a 1-bit number from a 1-bit number. According to the function, we only borrow from bit 1 iff we have for bit 0, and .
For the induction step, we assume the theorem is true up to an n-bit subtraction. In other words, we assume that we borrow one from bit only if . Furthermore, let be the borrow bits where is the borrow from bit .
Now we are ready to perform the last step. From the structure of a subtraction, the borrow from bit is as follows:
We have seen this derivation from a previous module, but the significance is different here.
In order for to be a one, can be true. But this means , which also means .
If is not true, then has to be true if is true. Let’s see what this means.
is true iff because we already know at this point is false. A true table can be used to illustrate this. We will also need to be true. Our induction assumption says that iff .
Because we also know that , we can now conclude that . This concludes the proof by induction.
The bottom line is that iff .
It is a little more difficult to figure out the ordering of signed values represented in binary. Intuitively, if , then , and so the sign bit of the result (difference) should be set.
This is true in some cases. For example, . In four-bit signed representations, and . The subtraction yields, , and the sign bit is set.
However, in some other cases, the sign flag is not set when “it is supposed to.” Let us take a look at this comparison: . In four-bit signed representations, , . , the sign bit of the result is not a one!
This can be explained by , and that is less than the most negative value that can be represented in four bits. In other words, we have an “overflow” situation. Because cannot be represented by four bits, it “wraps” around to the representation of .
Let us use the notation that is the minuend, is the subtrahend, is the difference, and subscripts indicate bit position. Assume all values are in bit representation. Then the overflow flag (it is a one iff a the result of a subtraction exceeds the limits of representations of signed integers) is defined as .
This means we have an overflow condition whenever the sign of subtrahend and difference are the same, and it is opposite to the sign of the minuend. This makes sense because it means or
Although the sign flag of the difference alone is not sufficient to indicate ordering of signed values represented in binary, together the flags can. We can define a new flag to indicate the minuend is less than the subtrahend.
One way to interpret the definition of is that when , it means “the proper flag should be the opposite”. After a subtraction of signed values represented in binary, we want either “the flag is set and it is proper” or “the flag is cleared, but should have been the opposite!”