3

I am implementing a VHDL 8 bit fixed point multiplication module which returns an 8bit truncated number but I have a problem when I do multiplications by hand in order to test it. The problem arises when I want to multiply two negative numbers.

I tried multiplying two positive values 1.67 * 0.625 ~ 1.04(0.906 in binary multipication).

001.10101 -> 1.67
000.10100 -> 0.625
------------
000000.1110111010 = 000.11101 (truncated to 8bits = 0.906)

I tried multiplying negative and positive numbers (-0.875 * 3 ~2.62)

111.00100 -> -0.875
011.00000 -> 3
----------
010101.0110000000 = 101.01100 (truncated to 8bits = -2.625)

So far everythng is working properly. The problem comes hen I try to multiply two negative numbers. According to what I know (unless I'm mistaken): - multiplying two numbers will give a result with twice the resolution (multiply two 8 bit numbers and you get a 16 bit number) - the fixed point gets dislocated as well. In this example there are 3 bits before the fixed and 5 points after. This means that in the resulting number the fixed point will have 6 digits before the point and 10 bits after the point.

By assuming this the above calculations worked properly. But when I try to multiply two negative values (-0.875 * -1.91 ~ 1.67)

110.00010 -> -1.91 (1.9375)
111.00100 -> -0.875
------------
101011.0011001000 = 011.00110(truncated to 8 bits = 3.1875)

Naturally, I tried another negative multiplication (-2.64 * -0.875 = 2.31)

101.01011 -> -2.64
111.00100 -> -0.875
----------
100110.0001001100 = 110.00010 (truncated to 8bits = -1.9375)

Clearly I'm doing something wrong, but I just can't see what I'm doing wrong.

PS: I haven't implemented it yet. The thought came to me I figured out how I was going to do it and then I tried to test it by hand with some simple examples. And I also tried more multiplications. I thought that maybe they worked out because I was lucky, but apparently not, I tried a few more multiplications and they worked. So maybe I'm doing something wrong when multiplying two negative numbers, maybe I'm truncating it wrong? Probably.

EDIT: Ok, I found a Xilinx document that states how multiplication is made when the two operands are negative, here is the link. According to this docuent, in order to this document, this can only be done when doing extended multiplication. And the last partial sum for the multiplication must be inverted and then add 1 to it and it will result in the correct number.

In order to the multiplications I used windows' calculator in programmer mode, which means that in order to multiply the 8 bits I put the numbers in the calculator and then got the result and truncated it. If they worked for the other cases it means that the windows calculator is doing a direct multiplication (adding all the partial sums as they should be instead of inverting the last partial sum). So, this means that in order to obtain the real result I should substract the first operand from the final result and then add the first operand inverted + 1

110.00010 -> -1.91 (1.9375)
111.00100 -> -0.875
------------
101011.0011001000
Which gave me the result: 000010.0111001000 = 010.01110(truncated to 8bits =2.43)

And the with the other one I came up with the result of 1.875. Those outputs aren't exactly great, but at least they are closer to what I expected. Is there any other way to do this in an easier way?

morcillo
  • 1,091
  • 5
  • 19
  • 51
  • unsigned or signed you have to allow for twice the size in the result or have one large one small. in decimal 1000*1000 = 1000000 could get closer with 9999 but easy to see that you have to look at the power of the most significant digits and the result of just those two digits determines within one the size of the result 2^7 * 2^7 = 2*14 a multiply of 8 bit numbers will require a 15 or 16 bit result, or just try 0xFF*0xFF and you get 0xFE01. if the msbits were for example 2^7 * 2^0 = 2^7 then you need either an 8 or 9 bit number for the answer, in this case 8 0xFF * 0x01 = 0xFF. – old_timer Jan 26 '16 at 20:17
  • @RyanVincent Sorry it took me such a long time to answer. I was doing my parallel research and posting my results. No need to detect and handle overflow. I know that doing it by ignoring the signs is doable, but I wanted to know if there was any other way that I could do it without ignoring them. According to my research there is but it's more difficult. I guess I'll have to do in if statement. if operand[7] = '1' and operand2[7] = '1' then input to the multiplier receives '0' on both bits7 and if they are diferferent then put the entire operand. That sucks .. I think – morcillo Jan 26 '16 at 20:18
  • @dwelch I know that. But I know that this particular system I want to apply it to will never output a result higher than 3 and lower than -4, that's why I did that truncation. I know that the resulting number can be represented in 8 digits, three before the point and 5 after the point. I also have two systems to test. One of them will NEVER multiply two negative inputs and the other will. I know that one of my systems will work this way. The other one won't. That's why I wanted to fix this. I tested the truncation a long time ago and it worked for the first systema (worked "perfectly") – morcillo Jan 26 '16 at 20:22
  • @RyanVincent I think I probably didn't understand your first question then. English isn't my first lnguage and I probably said something wrong. Thank you for your help, I'll keep on studying fo the rest of the day and I'll implement it tomorrow. Hopefully I'll get it to work. PS: I didn't understand the guess you wrote in your first post, so I jumped over it and took it as a question instead of a directon to follow. Sorry – morcillo Jan 26 '16 at 20:26
  • Definitely not an expert on multiplication, I always use embedded multipliers. However, I know that embedded multiplier use Booth's algorithm, and from what I recall of it it's both more efficient and deals with 2's complement multiplication. Note that "more efficient" doesn't always translate well to FPGA technologies, but you may want to look at it! – Jonathan Drolet Jan 27 '16 at 00:51
  • @JonathanDrolet Thank you. Completely forgot that there are dsp slices. I just got it in my mind that I had to implement it by hand. I do that ... get an idea and don't even think of the other possibilities. I'll look into it – morcillo Jan 27 '16 at 01:09

1 Answers1

3

Your intermediate results are wrong, so that, the truncation did not work as expected. Moreover, the truncation is only possible without overflow if the four top-most bit of the intermediate result are equal in your format. You should use signed data-types to do the multiplication right.

Even your second example is wrong. The intermediate binary result 010101.0110000000 represents the decimal number 21.375 which is not the product of -0.875 and 3. So, let's do the multiplication by hand:

    a     *     b      = -0.875 * 3 = -2.625
111.00100 * 011.00000
---------------------
          .  00000000  // further lines containing only zeros have been omitted
+         .01100000
+      011.00000
+     0110.0000
+   110100.000         // add -(2^2) * b  !
=   111101.0110000000  = -2.625  (intermediate result)
=      101.01100       = -2.625  after truncation

You have to add the two's complement of b in the last partial sum because the '1' in the top-most bit of a represent the value -(2^2) = -4. Truncation without overflow is possible here because the 4 top-most bits of the intermediate result are equal.

And now the third example

    a           b      = -1.9375 * -0.875 = 1.6953125
110.00010 * 111.00100 
--------------------- 
          .  00000000  // further lines containing only zeros have been omitted
+   111111.111100100   // sign-extended partial-sum
+   111110.0100        // sign-extended partial-sum
+   000011.100         // add -4 * b
=   000001.101100100   = 1.6953125 (intermediate result)
~      001.10110       = 1.6875 after truncation

As b is a signed number, one has always sign-extend the partial sum to the width of the intermediate result. Of course, this has also been done in the calculation of the second example, but there it does not make a difference.

Martin Zabel
  • 3,589
  • 3
  • 19
  • 34
  • Ok. I knew there was a high probability there was something with my way of thinking. BUUUT, I tested multiplying many values (both positive, positive and negative and both negative). I only had problems when both numbers were negative, all other tests worked (after truncation). I understand what you're telling me, but in my case all multiplications were correct after truncating them (except both negative) and I know that one of the operand will vary from 0 to 1 and the other will never be higher than 3. All my tests worked. Was it a coincidence? I really liked your answer. – morcillo Jan 28 '16 at 16:28
  • Thank you for taking your time to answer my question. – morcillo Jan 28 '16 at 16:29
  • @morcillo You liked my answer but didn't up-voted it? – Martin Zabel Jan 28 '16 at 17:17
  • Oops. Sorry. Up voted now. But do you know why it works in my cases when truncated? Luck? Specific case? – morcillo Jan 28 '16 at 17:27
  • @morcillo It was just luck. If you multiply `111.0` (-1) by `011.1` (3.5) the correct result after truncation is `100.1` (-3.5). But without complementing the last partial sum, you would get `000.1` which has even a wrong sign. – Martin Zabel Jan 28 '16 at 20:11
  • Good to know. Question completely answered. Thank you – morcillo Jan 28 '16 at 21:01