7

Even though I read a number of articles that say that mostly 2's complement is used to represent the negative numbers in a signed integer and that that is the best method,

However for some reason I have this (below) stuck in my head and can't get rid of it without knowing the history of it

"Use the leading bit as 1 to denote negative numbers when using signed int."

I have read many posts online & in StakOverflow that 2's complement is the best way to represent negative numbers. But my question is not about the best way, it is about the history or from where did the "leading bit" concept arise and then disappear?

P.S: Also it is just not me, a bunch of other folks were also getting confused with this.

Edit - 1 The so called leading 1 method I mentioned is described with an example in this post: Why is two's complement used to represent negative numbers?

Now I understand, the MSB of 1 signifies negative numbers. This is by nature of 2's complement and not any special scheme.

Eg. If not for the 1st bit, we can't say if 1011 represents -5 or +11.

Thanks to: jamesdlin, Oli Charlesworth, Mr Lister for asking imploring questions to make me realize the correct answer.

Rant: I think there are a bunch of groups/folks who have been taught or been made to think (incorrectly) that 1011 evaluates to -3. 1 denoting - and 011 denoting 3.

The folks who ask "what my question was.. " were probably taught the correct 2's complement way from the first instance they learnt it and weren't exposed to these wrong answers.

Community
  • 1
  • 1
Kingkong Jnr
  • 1,128
  • 4
  • 18
  • 29
  • 1
    Some decent pointers to the history of two's and one's complement arithmetic are in this closed SO question: http://stackoverflow.com/questions/8041674/twos-complement-history – Michael Burr May 03 '12 at 06:13
  • 4
    Ask yourself this: you have an integer type of *n* bits, but you want to store negative and non-negative numbers. How would *you* do it? It'd make sense to divide your integer range in half and map half to negatives, half to non-negatives. Representing non-negative numbers is straightforward, and there's no reason why that shouldn't just be a direct mapping. That's one half of your range. The other half happens to have 1 as the most significant bit. – jamesdlin May 03 '12 at 06:14
  • Note that using complements is a technique used in non binary systems as well: http://webhome.idirect.com/~totton/soroban/Negative%20numbers.pdf – Michael Burr May 03 '12 at 06:33
  • 2
    I don't understand what the question is here. Are you asking "why/when was [sign-magnitude](http://en.wikipedia.org/wiki/Sign-magnitude#Sign-and-magnitude_method) invented?". Well, it's such a natural mechanism it was probably invented multiple times. And as for "disappear", well, it hasn't disappeared. – Oliver Charlesworth May 03 '12 at 07:33
  • 1
    Agreed with @OliCharlesworth. Please clarify which system of representing signed ints is being discussed. All the answers thus far discuss two's complement because that is mentioned in the question, but the 'leading bit' term may or may not refer to two's complement, since one's complement, two's complement, sign-magnitude, and bias/excess representations ALL end up signaling the sign of the number using the most significant bit. – Brian L May 04 '12 at 00:54
  • 1
    2's complement also follows from basic number theory. If you consider the ring of integers mod 2**n. The larger numbers are congruent to the negative numbers. By choosing the partition between positive and negative just right, the top bit ends up indicating sign. – phkahler May 04 '12 at 19:35

5 Answers5

4

There are several advantages to the two's-complement representation for signed integers.

Let's assume 16 bits for now.

Non-negative numbers in the range 0 to 32,767 have the same representation in both signed and unsigned types. (Two's-complement shares this feature with ones'-complement and sign-and-magnitude.)

Two's-complement is easy to implement in hardware. For many operations, you can use the same instructions for signed and unsigned arithmetic (if you don't mind ignoring overflow). For example, -1 is represented as 1111 1111 1111 1111, and +1 as 0000 0000 0000 0001. If you add them, ignoring the fact that the high-order bit is a sign bit, the mathematical result is 1 0000 0000 0000 0000; dropping all but the low-order 16 bits, gives you 0000 0000 0000 0000, which is the correct signed result. Interpreting the same operation as unsigned, you're adding 65535 + 1, and getting 0, which is the correct unsigned result (with wraparound modulo 65536).

You can think of the leading bit, not as a "sign bit", but as just another value bit. In an unsigned binary representation, each bit represents 0 or 1 multiplied by the place value, and the total value is the sum of those products. The lowest bit's place value is 1, the next lower bit is 2, then 4, etc. In a 16-bit unsigned representation, the high-order bit's place value is 32768. In a 16-bit signed two's-complement representation, the high-order bit's place value is -32768. Try a few examples, and you'll see that everything adds up nicely.

See Wikipedia for more information.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Yeah, one just needs to think about negative numbers like this: what is -1? It's what you add 1 to to get 0. Naturally, in binary it looks like this 11...11 + 00...01 = 00...00 (just as in your example), and all negative numbers have the most significant bit set to 1 in this representation. – Alexey Frunze May 03 '12 at 08:26
2

It's not just about the leading bit. It's about all the bits.

Starting with addition

First let's look at how addition is done in 4-bit binary for 2 + 7:

  10 + 
 111
____
1001

It's the same as long addition in decimal: bit by bit, right to left.

  • In the rightmost place we add 0 and 1, it makes 1, no carry.
  • In the second place from the right, we add 1 and 1, that makes 2 in decimal or 10 in binary - we write the 0, carry the 1.
  • In the third place from the right, we add the 1 we carried to the 1 already there, it makes binary 10. We write the 0, carry the 1.
  • The 1 that just got carried gets written in the fourth place from the right.

Long subtraction

Now we know that binary 10 + 111 = 1001, we should be able to work backwards and prove that 1001 - 10 = 111. Again, this is exactly the same as in decimal long subtraction.

1001 -
  10
____
 111

Here's what we did, working right to left again:

  • In the rightmost place, 1 - 0 = 1, we write that down.
  • In the second place, we have 0 - 1, so we need to borrow an extra bit. We now do binary 10 - 1, which leaves 1. We write this down.
  • In the third place, remember we borrowed an extra bit - so again we have 0 - 1. We use the same trick to borrow an extra bit, giving us 10 - 1 = 1, which we put in the third place of the result.
  • In the fourth place, we again have a borrowed bit to deal with. Subtract the borrowed bit from the 1 already there: 1 - 1 = 0. We could write this down in front of the result, but since it's the end of the subtraction there's no need.

There's a number less than zero?!

Do you remember how you learnt about negative numbers? Part of the idea is that you can subtract any number from any other number and still get a number. So 7 - 5 is 2; 6 - 5 is 1; 5 - 5 is 0; What is 4 - 5? Well, one way to reason about such numbers is simply to apply the same method as above to do the subtraction.

As an example, let's try 2 - 7 in binary:

     10 -
    111
_______
...1011

I started in the same way as before:

  • In the rightmost place, subtract 1 from 0, which requires a borrowed bit. 10 - 1 = 1, so the last bit of the result is 1.
  • In the second-rightmost place, we have 1 - 1 with an extra borrow bit, so we have to subtract another 1. This means we need to borrow our own bit, giving 11 - 1 - 1 = 1. We write 1 in the second-rightmost spot.
  • In the third place, there are no more bits in the top number! But we know we can pretend there's a 0 in front, just like we would do if the bottom number ran out of bits. So we have 0 - 1 - 1 because of the borrow bit from second place. We have to borrow a bit again! Anyway we have 10 - 1 - 1 = 0, which we write down in the third place from the right.
  • Now something very interesting has happened - both the operands of the subtraction have no more digits, but we still have a borrow bit to take care of! Oh well, let's just carry on as we have been doing. We have 0 - 0, since neither the top or bottom operand have any bits here, but because of the borrow bit it's actually 0 - 1.

    (We have to borrow again! If we keep borrowing like this we'll have to declare bankruptcy soon.)

    Anyway, we borrow the bit, and we get 10 - 1 = 1, which we write in the fourth place from the right.

Now anyone with half a mind is about to see that we are going to keep borrowing bits until the cows come home, because there ain't no more bits to go around! We ran out of them two places ago if you forgot. But if you tried to keep going it'd look like this:

...00000010
...00000111
___________
...11111011
  • In the fifth place we get 0 - 0 - 1, and we borrow a bit to get 10 - 0 - 1 = 1.
  • In the sixth place we get 0 - 0 - 1, and we borrow a bit to get 10 - 0 - 1 = 1.
  • In the seventh place we get 0 - 0 - 1, and we borrow a bit to get 10 - 0 - 1 = 1.

...And so it goes on for as many places as you like. By the way, we just derived the two's complement binary form of -5.

You could try this for any pair of numbers you like, and generate the two's complement form of any negative number. If you try to do 0 - 1, you'll see why -1 is represented as ...11111111. You'll also realise why all two's complement negative numbers have a 1 as their most significant bit (the "leading bit" in the original question).

In practice, your computer doesn't have infinitely many bits to store negative numbers in, so it usually stops after some more reasonable number, like 32. What do we do with the extra borrow bit in position 33? Eh, we just quietly ignore it and hope no one notices. When some does notice that our new number system doesn't work, we call it integer overflow.

Final notes

This isn't the only way to make our number system work, of course. After all, if I owe you $5, I wouldn't say that your current balance with me was $...999999995.

But there are some cool things about the system we just derived, like the fact that subtraction gives you the right result in this system, even if you ignore the fact that one of the numbers is negative. Normally, we have to think about subtractions with conditional steps: to calculate 2 - 7, we first have to figure out that 2 is less than 7, so instead we calculate 7 - 2 = 5, and then stick a minus sign in front to get 2 - 7 = -5. But with two's complement we just go ahead do the subtraction and don't care about which number is bigger, and the right result comes out by itself. And others have mentioned that addition works nicely, and so does multiplication.

Brian L
  • 3,201
  • 1
  • 15
  • 15
  • An interesting read, but not an answer to the leading bit, which was the question. – Matsemann May 03 '12 at 09:18
  • @Matsemann, the answer to why "leading bit" is 1 for negative numbers is implied in the fourth-last paragraph. I concede I should have made it more obvious. – Brian L May 04 '12 at 00:41
  • One of my favorite observations related to two's-complement math is that if one uses the power-series function to compute the value of `...11111` [i.e. 1+2+4+8+16+32+64...] the non-converging series sums to -1. – supercat Jun 20 '13 at 23:28
1

You don't use the leading bit, per say. For instance, in an 8-bit signed char,

11111111

represents -1. You can test the leading bit to determine if it is a negative number.

There are a number of reasons to use 2's complement, but the first and greatest is convenience. Take the above number and add 2. What do we end up with?

00000001

You can add and subtract 2's complement numbers basically for free. This was a big deal historically, because the logic is very simple; you don't need dedicated hardware to handle signed numbers. You use less transistors, you need less complicated design, etc. It goes back to before 8-bit microprocessors, which didn't even have multiply instructions built-in (even many 16-bit ones didn't have them, such as the 65c816 used in apple IIe and Super NES).

With that said, multiplication is relatively trivial with 2's complement also, so that's no big deal.

std''OrgnlDave
  • 3,912
  • 1
  • 25
  • 34
1

Complements (including things like 9s complement in decimal, mechanical calculators / adding-machines / cash registers) have been around forever. In nines' complement with four decimal digits, for instance, values in the range 0000..4999 are positive while values in 5000..9999 are negative. See http://en.wikipedia.org/wiki/Method_of_complements for details.

This directly gives rise to 1s complement in binary, and in both 1s and 2s complement, the topmost bit acts as a "sign bit". This does not explain exactly how computers moved from ones' complement to two's complement (I use Knuth's apostrophe convention when spelling these out as words with apostrophes, by the way). I think it was a combination of luck, irritation at "negative zero", and the way ones' complement requires end-around carry (vs two's complement, not requiring it).

In a logical sense, it does not matter which bit you use to represent signs, but for practical purposes, using the top bit, and two's complement, simplifies the hardware. Back when transistors were expensive, this was pretty important. (Or even tubes, although I think most if not all vacuum-tube computers used ones' complement. In any case they predated the C language by rather a lot.)

In summary, the history goes back way before electronic computers and the C language, and there was no reason to change from a good way of implementing this mechanically, when converting from mechanical calculators to vacuum-tube ENIACs to transistorized computers and then on to "chips", MSI, LSI, VLSI, and onward.

torek
  • 448,244
  • 59
  • 642
  • 775
  • In nines'-complement, the value 9998 would be -1 and the value 9999, if it arises, would be 0. In ten's-complement, 9998 is -2 and 9999 is -1. Note that nines'-complement subtracts each digit from 9 (hence *nines'* is plural) while ten's-complement subtracts the first digit from 10 (hence *ten's* is singular). – supercat Dec 29 '16 at 18:31
  • @supercat: right, that's Knuth's reasoning for the apostrophe placement as well. I see I forgot it entirely in the earliest occurrence of "nines' complement" though! Will fix. – torek Dec 29 '16 at 18:38
0

Well, it had to work such that 2 plus -2 gives zero. Early CPUs had hardware addition and subtraction and someone noticed that by complementing all the bits (one's complement, the original system), to change the "sign" of the value, it allowed the existing addition hardware to work properly—except that sometimes the result was negative zero. (What is the difference between -0 and 0? On such machines, it was indeterminate.)

Someone soon realized that by using twos-complement (convert a number between negative and positive by inverting the bits and adding one), the negative zero problem was avoided.

So really, it is not just the sign bit which is affected by negatives, but all of the bits except the LSB. However, by examining the MSB, one can immediately determine whether the signed value there is negative.

wallyk
  • 56,922
  • 16
  • 83
  • 148
  • Complementary arithmetic surely predates electronic computers. See http://h2g2.com/dna/h2g2/A1920511 and http://infohost.nmt.edu/~borchers/tenscomp.pdf – Michael Burr May 03 '12 at 06:43
  • Also, you sound like they gave up the "one bit for the sign" concept long ago. However, that is not the case. Half your computer still works like that, and yes, it can have 0 and -0! – Mr Lister May 03 '12 at 06:45
  • @MrLister: I did not intend any such thing. I worked with a ones-complement machine in the 1970s, and it sometimes sucked to check for both -0 and +0. I don't understand your other statements. What is the twos-complement representation of an 8-bit negative zero? – wallyk May 03 '12 at 07:00
  • @MichaelBurr: Yes it does. While adding machines did something like nines-complement arithmetic for subtraction and negative numbers, they weren't concerned about the time and space requirements of checking variations of zero. So it did not compel a better system. – wallyk May 03 '12 at 07:05