1

I have a trouble with understanding why we discard MSB when we do multiplication of 2's complements numbers.

Let's take 101 (decimal: -3) and 011 (decimal: 3) for example. Algorithm goes like this: first we double length of our numbers, then we do usual multiplication as we did in school with decimals, then we take (doubled length)*2 = 6 least significant bits.

  1. Double lengths:

    101 -> 111101
    011 -> 000011
    
  2. Do multiplication: 111101 * 000011 = 10110111 (decimal: -73)

As we see, this result is wrong.

  1. Take 6 least significant bits (drop 2 most significant bits). 10110111 -> 110111 (decimal: -9)

And so result became magically right. How can this be explained? I understand that MSB is kind of special and the same rules I've used in school cannot be 100% suitable for 2's complements, but while I fully understand school rules of multiplication, I can't wrap my head around the last step of 2's complement multiplication (first two steps I understand).

Vikrant
  • 4,920
  • 17
  • 48
  • 72
popfalushi
  • 1,332
  • 9
  • 15
  • addition and subtraction in 2's complement also throw the high bits – phuclv Dec 01 '16 at 08:41
  • At least in addition, it is called overflow and it sucks ) It produces semantically wrong (127+1 != -128) but technically understandable result (variable has its size, addition is mechanical job with its laws, so sometimes overflow happens). In multiplication we produce semantically right results when we drop MSBs, and it is magic for me. – popfalushi Dec 01 '16 at 10:30
  • in addition you need to drop the MSB to get the correct result, too. It's a wrapped around result – phuclv Dec 01 '16 at 10:35
  • Yes, as I said, it is called overflow and it produces semantically wrong but technically understandable result. – popfalushi Dec 01 '16 at 11:19
  • don't think about it as dropping 2 most significant bits, because there are 6 most significant bits (4 more '0's because there are 4 '0's in the 000011). It's about non-widening multiplication 6x6->6 bits. The 6 high bits will be different in signed and unsigned multiplication – phuclv Dec 01 '16 at 16:25

3 Answers3

5

Your multiplication is simply incorrect. You did an unsigned multiplication. In other words this:

    111101  
    000011 x
    ------ 
    111101
   111101
  000000
 000000
000000     +
----------
0010110111

Or in decimal 61 x 3 = 183. But a signed multiplication requires extending the sign bit in the partial products as well. Like this:

    111101  
    000011 x
    ------ 
1111111101
111111101 
00000000
0000000
000000     +
----------
1111110111

So now you correctly compute -3 x 3 = -9. This distinction matters for processors as well, compare MUL vs IMUL on Intel processors.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • it's actually a **correct** but inefficient way and is described in [wikipedia](https://en.wikipedia.org/wiki/Two's_complement#Multiplication) – phuclv Dec 07 '16 at 01:40
  • thank you very much! The fact that partial products must be extended is very logical indeed, and with it everything becomes very intuitive. – popfalushi Dec 07 '16 at 10:27
1

That's just about arithmetics. It's not semantically wrong because it's actually a correct modulo result, as operations on integers in computers result in the discarding of the high bits which is similar to modulo 2k in a k-bit computer.


First, you should know that non-widening multiplication is the same for both unsigned and 2's complement signed number with the same bit pattern.

For example: 10012 x 00112.

  • Treating as unsigned we have 9 x 3 = 27 (0001 10112)
  • Treat them as signed we have -7 x 3 = -21 (1110 10112)

You can see a specific case here. For the general case take 2 positive k-bit numbers m and n for example. That means their negative values -m and -n will be represented by 2k - m and 2k - n§ respectively. Now we multiply those 2 negative values to get a k-bit result:

(-m)(-n) mod 2k
= (2k - m)(2k - n) mod 2k
= [22k -2k(m+n) + mn] mod 2k
= mn mod 2k
=(-m)(-n) mod 2k

The same in case one number is positive and the other is negative

So regardless of treating two bit patterns as signed or unsigned, the result is still the same.


Now we have two n-bit unsigned numbers and do multiplication on them, we'll get a 2n-bit number, because multiplying an n-bit number and an m-bit number producing an (n+m)-bit result.

But if we only care about the low n-bits of the result then it's exactly the same as the result of two n-bit unsigned numbers with the same bit pattern, as proved above.

So we take two (n/2)-bit signed numbers, sign-extend them to n-bits (your 1st step) and do non-widening multiplication (your 2nd step) to get an n-bit result. What we have now is exactly the same as signed widening multiplication from n/2 bits to n bits.

Doubling the bits in the signed operands is effectively just making them as wide as the final result so that the multiplication is now an unsigned non-widening instead of a signed widening one as before.

If you notice, the high bits of the signed and unsigned versions will have relation to each other too, if you look at the proof above

§Because that's how two's complement is defined:

The two's complement of an N-bit number is defined as the complement with respect to 2N; in other words, it is the result of subtracting the number from 2N

Similar to 1's complement and 2's complement in binary, in decimal we have 9's complement and 10's complement to represent negative numbers although it's not used in daily life and is out of scope of this problem

Community
  • 1
  • 1
phuclv
  • 37,963
  • 15
  • 156
  • 475
  • Not quite understood you. I understand that I can substitue phrase "leave k LSBs" with "let's get mod2^k" - it is the same process. Why we take modulo? Why result without modulo is wrong and result with it is right? Additionally, I don't quite get why `-m = 2^k - m`. It seems wrong to me. m can be encoded in infinite number of bits, so we have here infinite 2^k and constant m. – popfalushi Dec 01 '16 at 12:31
  • because the result is only n-bits. It doesn't take infinite number of bits – phuclv Dec 01 '16 at 12:32
  • Well, we came to the place we ended in comments to the question. I said that explanation can't be "those bits exceed quota, so they are garbage and we can discard them and the rest is magically the right answer" because it is not math. Imagine you inventing these principles: you invented encoding of 2's complement, and now think 'how will multiplication work?'. You must have some mathematical proof that it is you'll get right result, and "I just need value <= 2^k, so I do `mod`" is not a proof. – popfalushi Dec 01 '16 at 12:41
  • First, if you always assume that maths must always follow normal arithmetics then you're out of luck. Computers don't work like that. For example double `1e30+1e-30` doesn't give you what you expected. Similarly unsigned maths won't be as what one does on paper (5+6 doesn't give you 11 in a 3-bit operation) because they work on a [ring](https://en.wikipedia.org/wiki/Ring_(mathematics)) or a [field](https://en.wikipedia.org/wiki/Field_(mathematics)). – phuclv Dec 01 '16 at 14:27
  • About this problem, you must know that all calculations on 32-bit int will always result in a low 32-bit result, discarding all higher bits, which is exactly as modulo 2^32. So that's not counter what I said – phuclv Dec 01 '16 at 14:27
  • Well, you seem to know your stuff, but I still don't understand ) Anomalies with floating point arithmetics can be explained with how it is implemented. Once again: if I add 0001 to 1111 and width is 4 bits it is perfectly understandable why result is 0000. Overflow happens, it is technical restriction. To overcome this restriction and get semantically correct result in Java we use BigInteger - it does not overflow and we get what every sane person expect - 10000. But in multiplication we somehow get smth like 10000, but 0000 is somehow right answer both semantically and technically. – popfalushi Dec 01 '16 at 14:55
  • no multiplication can result in 0 without either of the operands being 0. It's not magically appear, it's just the high 6 bits dropped and low 6 bits remain – phuclv Dec 01 '16 at 16:37
  • 0000 was a bad example - it just corresponded with addition in previous sentence. I meant in addition BigInteger allows us avoid overflow so we get semantically right results and do not throw anything away. In multiplication we get semantically right result by throwing away. Strange ) – popfalushi Dec 01 '16 at 18:11
  • it's not strange, as I said the bits are dropped because modular arithmetic, exactly like wraparound in unsigned integer. You haven't written enough digits so you might think it's strange. Multiplying 2 6-bit numbers always produce 12 bits of result. In your case `111101 * 000011 = 000010 110111` so discarding the high 6 bits we'll have a low same 6 bits for both signed and unsigned. The trick is simply making a signed multiplication into an unsigned one so normal multiplication works. To make that work you have to make the operands wide enough. Please read the explanation again – phuclv Dec 02 '16 at 02:12
  • `I don't quite get why -m = 2^k - m. It seems wrong to me. m can be encoded in infinite number of bits, so we have here infinite 2^k and constant m` No, 2's complement only works with a fixed-width number (no infinite k) so a value has no meaning without knowing how many bits are used to represent it. The `2^k - m` is how 2's complement is defined. Please read the second line in the 2's complement wiki (quoted in my answer) – phuclv Dec 02 '16 at 02:54
0

Multiplication is just repeated addition.

When we sum two n-bit numbers the result has (n+1)-bit as there can be at most one carry on the MSb.
When we multiply two n-bit numbers the result has (n+n)-bit as we add a number to itself at most 2n times and that produces at most 2n carries and those takes log2(2n) = n bits.

Since we work by adding 2n-bit numbers we need to sign extend the operands to twice their size.
This is a standard rule when growing the size of two's complement number.

If we were to use this inefficient algorithm we won't need to throw away any bit.


The grade school algorithm for multiplying a and b with b = bk bk-1 ... b1 b0 exploits the distributive property as a * b = a * b0 + a * b1 * B + a * b2 * B2 + a * b3 * B3 + ... where B is the base of the numbers.

This is useful as in binary a digit bi is either 0 or 1 so that a number is either added (if bi = 1) or it isn't (if bi) = 0). Furthermore the powers Bi are powers of two so they can be implemented as a shift.

Now, since the final result a * b must have at least 2n-bit each of the addend a * bi * Bi must be of size 2n-bit. (again this is well explained in your question) However when we use a shift to perform the multiplication by Bi that introduces additional bit beyond the 2n required.
For example:

  |
  111 111
  |   011
  -------
  |    11 
  |   11
  |  11
  |11
  11
 11
  |

These bits are just an artifact of the fact that, as humans, it's more simple to just discard the surplus of bits after we have done all the sums rather than, in each sum, stopping after we reach the 2n-th bit.

In practice hardware do the latter, they have 2n-bit registers where they extend the operands and during the shifting the surplus bit are discarded (just like a normal shift). The register that accumulates the sums also discard the carry (just like a normal adder).

+-------+   +-------+   +-------+   +-------+   +-------+   +-------+    
|000 011| + |000 110| + |001 100| + |011 000| + |110 000| + |100 000|
+-------+   +-------+   +-------+   +-------+   +-------+   +-------+

=

+-------+
|111 101| Correct result with no bits discarded (but for the sums carries)  
+-------+
Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124