Pfhorrest wrote:(and of having signed ints with the sign bit set not equal to the negative of the same int without the sign bit set, but rather one number less than that, e.g. 011111112 = 3276710 but 11111112 = -3276810) are just hacks designed to fit one more item into a list or array, and not something we should be emulating in the general world where we're not always trying to cram as much data as possible into as few binary digits as possible. We can count from 1 as is natural, rather than 0; and we can afford to have "-x" mean just the additive inverse of x as is natural, rather than making it mean one less than the additive inverse of x just because "-0" is redundant with "0".
The "negative zero" thing has existed in the past; it's called a one's-complement architecture
. Signed integers without negative-zero are two's-complement
. The logic circuits that do math in two's-complement are simpler than in one's-complement. So it's a "hack" (read: elegant implementation) for speed and efficiency, not for "holding one more item" (or holding an item one larger, assuming it's negative.)
Also, what Pfhorrest describes isn't actually one's or two's complement. Having 11111111 represent -127 is sign and magnitude. In two's complement, 11111111 represents -1 and 10000000 represents -128. To convert a positive number to a negative one, you invert all the bits
and add one. This sounds kind of crazy, but as Mikeski points out, two's complement makes arithmetic simpler, at least addition and subtraction.
It's easy to build adders and counters that work modulo M=2N
: basically that's what you get by default if you just let the last carry go hang. Numbers in Z that differ by M are indistinguishable, and when you define a number as two's complement, all you're doing is defining which range of Z you consider the values to represent. It can help to think of numbers as being like angles. We can decide to have all angles be positive, so we have angles in the range 0 to 359 degrees. Or we can decide that we will call anticlockwise angle up to half a turn positive, and that clockwise angles are negative, hence an angle represents a number in the range -180 degrees to 179 degrees. Adding a positive angle is just an anticlockwise rotation, and adding a negative angle is clockwise. But we know that a clockwise rotation of 90 degrees is just the same as an anticlockwise rotation of 270 degrees, so the same angle that represents +270 in unsigned represents -90 in signed. Note how the difference is just in our interpretation. The angles go on doing their thing irrespective of whether we call them signed or unsigned.
The same thing happens with adders - the circuit is the same whether the numbers are signed or unsigned. The only difference is if you want to detect overflow, in which case you need to examine the carries into and out of the most significant bit. What you're doing is equivalent to making sure you didn't go through the +180/-180 discontinuity illegally.
I doubt that the extra code representing -M/2 was a motivation. Actually it's one of the inelegant
aspects of the system, since you are unlikely to want more negative range than positive, and when calculating -x you need to handle the case specially because there's no corresponding positive representation. In principle you could say that the 1000...0 code represents +M/2 instead, but you lose the elegance of the top bit being a sign bit and make detecting overflows harder.