0

I always thought signed multiplications and divisions needs different circuit-logic than the corresponding unsigned operations. On another forum there was someone who said, the unsigned-ALUs can be used without any modification for signed parameters and give valid results.

I just tested this with a little program

#include <stdio.h>

void main()
{
    short a, b;
     long long c;

    c = 0;
    a = -32768;
    do
    {
        b = -32768;
        do
            if( (int)((unsigned)a * (unsigned)b) != ((int)a * b) )
                ++c;
        while( ++b != -32768 );
    } while( ++a != -32768 );

    printf( "%.0lf\n", (double)c );
}

This prints zero, i.e. the multiplications work like the above assumption. So why there are different instructions for signed and unsigned multiplications and how is this represented in the circuit-logic?

And I didn't talk about divisions. Am I right to assume, that my earlier assumption that different circuit-logic is needed at least for divisions?

phuclv
  • 37,963
  • 15
  • 156
  • 475
Bonita Montero
  • 2,817
  • 9
  • 22
  • Multiplication giving the same size result as the operand doesn't care about signedness. The variants that give you the full double sized result do. – Jester Jan 16 '18 at 22:41
  • 1
    I don't know where you get that unsigned-ALU can be used for signed operations without modifications. That's simply wrong. Many operations (like add and non-widening mul) don't depend on the signness of the operands but some do, like widening mul/div although you can easily get the signed result from unsigned operation and vice versa [Why are signed and unsigned multiplication different instructions on x86(-64)?](https://stackoverflow.com/q/14063599/995714), [Which arithmetic operations are the same on unsigned and two's complement signed numbers?](https://stackoverflow.com/q/21475286/995714) – phuclv Jan 17 '18 at 01:36
  • 1
    That said, you can use the same logic with a little bit trick to do both signed and unsigned operations without doubling the circuit. And [`void main()` is wrong](https://stackoverflow.com/q/204476/995714) – phuclv Jan 17 '18 at 01:41
  • This is a good question, but note that the "testing" part with C is full of undefined behaviour [(signed integer overflow)](https://stackoverflow.com/questions/19842215/wrap-around-explanation-for-signed-and-unsigned-variables-in-c), in the multiply [(`-32768 * -32768` overflows a signed `int`)](https://stackoverflow.com/questions/40731543/implementing-enforcing-wraparound-arithmetic-in-c) and in the loop counter! If using gcc, [this code is safe with `-fwrapv`](https://stackoverflow.com/questions/47232954/what-does-fwrapv-do), I think. (It might happen to work without that, esp with `-O0`.) – Peter Cordes Jan 17 '18 at 04:31
  • "void main()", undefined behaviour of overflows - this all doesn't matter here. – Bonita Montero Jan 17 '18 at 08:23
  • matter or not, did you read the other questions? They have explained clearly why there are different mul/div instructions for signed and unsigned operations – phuclv Jan 23 '18 at 15:05

2 Answers2

3

It depends on the size of the parameters. if you multiply two 8 bit values it takes 16 bits to store a result worst case right? 0xFF * 0xFF = 0xFE01. 255*255 = 65025.

From grade school how do we do multiplication by hand? I have two, two bit numbers

   ab
*  cd
======

Lets say cd are both ones to make this obvious what is going on.

    ab
 *  11
======
   0ab
+  ab 
======

If we consider these to be unsigned numbers then the bits to the left of ab and cd are padded with zeros. but if these are signed numbers then those bits are sign extended

  000ab
* 000cd
========

  xxxab
* yyycd
=======

If I take two bit operands and a two bit result then this doesnt matter

    ab
 *  11
======
    ab
+   b 
======

the bits in the summation are dont cares. but if I want a two bit operands and a 4 bit result it matters.

Division is just the reverse you want to start with a 64 bit numerator if you want to have a 32 bit denominator, so 32 bits divided by 32 bits is that numerator signed or unsigned? When you pad it to 64 bits in order to do the division, do you sign extend it or zero pad it (signed or unsigned). the result varies.

Some instruction sets dont have a two to one the operands are all the same size so you "simply" have to synthesize this but you do it the way you would based on grade school knowledge.

addition and subtraction dont care about signed vs unsigned they dont know, subtract uses an adder, the beauty of twos complement, you invert and add one so you invert the second operand, and then feed a 1 into the carry in on the lsbit then add, turning an adder into a subtractor. twos complement takes care of the rest, the results just work either way. the unsigned overflow vs signed overflow do matter and if both are computed then your adder does both. unsigned overflow is the carry bit out of the msbit, signed is the comparision of the carry in and carry out of the msbit which can also be determined from the msbits of the input and the result.

old_timer
  • 69,149
  • 8
  • 89
  • 168
2

On another forum there was someone who said, the unsigned-ALUs can be used without any modification for signed parameters and give valid results.

To understand this contradiction we should keep in mind that a number (here a decimal number) has "implicit" digits at the left and the right:

12345 = 00...0000000012345.000000000...00

The same is true for positive and unsigned binary numbers; the implicit digits are 0 in this case:

01110100 = 00...0000000001110100.0000...00

For negative numbers stored as two's complement the implicit digits on the left side however are ones, not zeroes:

10110100 = 11...1111111110110100.0000...00

Whenever one of the "implicit" digits on the left side influences the result of an operation we need an unsigned and a signed variant of an operation:

Shift

Shifting left the implicit digit on the right side (after the decimal dot) will influence the result; this is always 0.

However shifting right the first implicit digit on the left side will influence the result; and indeed there are two "shift right" operations: "SRL" (unsigned) and "SRA" (signed).

Addition, subtraction and multiplication

In these cases the results will also differ:

1111 + 0011 = 11111 + 00011 = 00010 (signed)
1111 + 0011 = 01111 + 00011 = 10010 (unsigned)

However most CPUs provide operations that will only return as many bits as the input data had: If you are adding two 8-bit numbers you'll get only the lower 8 bits of the result, not the whole 9 bits. And the lower 8 bits do not differ between signed and unsigned operation.

If there is a CPU which can add two 8-bit numbers returning a 16-bit result this CPU requires an "unsigned add" and a "signed add" instruction!

The same is true for the multiplication. However a lot of (most) CPUs work like this: They multiply two 32-bit numbers and the result is not the low 32 bits but the full 64 bits - so a "signed multiply" and an "unsigned multiply" instruction are required.

There are however few CPUs that multiply two 32-bit numbers returning only the low 32 bits of the result. These CPUs will only need one "multiply" instruction.

Division

Dividing by 8 is the same operation as shifting right by three bits.

This means that three implicit digits from the left side will be shifted in which means that the implicit digits on the left side have an influence on the result.

Therefore you'll always need an "unsigned divide" and a "signed divide" operation.

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38