signed vs. unsigned multiplication

W

wrschlanger

Does anyone care to comment on the below program I wrote?
The question I was wondering is: why do compilers still use IMUL or MUL forsigned or unsigned multiplication respectively when there is no differencein the numeric value of the result provided the product size is the same as the size of the two factors that are being multiplied together?

This program prints PASS on my screen when I run it. Am I missing something?

Here is what I think is going on. The Intel manual lists IMUL for signed imultiplication and MUL for unsigned. It does this because originally those instructions always doubled capacity, i.e. the product had twice the size ofthe factors.

But then a variant of IMUL was introduced that discards the upper half of the result, i.e. it works like the C * operator. This is still listed as being for "signed" multiplication but it works equally well for unsigned multiplication and might be more efficient because it can take more operands.

Do any compiler writers know about this?? Or do they blindly trust the Intel manuals?

(By the way--interestingly I can't get (unsigned char)(0xff) to equal (signed char)(0xff) in GCC).
---
#include <cstdio>

int main()
{
for(int a = 0; a < 256; ++a)
{
for(int b = 0; b < 256; ++b)
{
signed char sa = a;
signed char sb = b;
unsigned char ua = a;
unsigned char ub = b;
signed char sc = sa * sb;
unsigned char uc = ua * ub;
unsigned char usc = sc;
if(uc != usc)
{
std::printf("FAIL %02x * %02x = %02x vs. %02x\n", ua, ub, (unsigned int)(unsigned char)(sc), uc);
break;
}
}
}
std::printf("PASS\n");
return 0;
}
 
X

Xavier Roche

Le 17/06/2012 16:31, (e-mail address removed) a écrit :
Does anyone care to comment on the below program I wrote?

There is no difference between signed/unsigned muls if you restrict them
to the source size AFAIK.
The question I was wondering is: why do compilers still use IMUL or MUL for signed or unsigned multiplication respectively when there is no difference in the numeric value of the result provided the product size is the same as the size of the two factors that are being multiplied together?

extern unsigned char mulu(unsigned char a, unsigned char b) {
return a*b;
}

extern signed char muls(signed char a, signed char b) {
return a*b;
}

$ gcc -O -S -std=c99 sample.c -o sample.S

mulu:
movl %esi, %eax
imull %edi, %eax
ret

muls:
movl %esi, %eax
imull %edi, %eax
ret
 
E

Eric Sosman

Does anyone care to comment on the below program I wrote?

Comment: It's written in C++, not C. The two languages are
related but not identical, and differ in details large and small.
Try comp.lang.c++ for your C++ questions.
(By the way--interestingly I can't get (unsigned char)(0xff) to equal (signed char)(0xff) in GCC).

This would be easy to explain in C, although I don't know whether
the C++ explanation would be similar. In C on a machine with eight-bit
characters, `signed char' cannot represent the value 0xFF=255 so the
conversion of 255 to `signed char' so "the result is implementation-
defined or an implementation-defined signal is raised." The usual
implementation-defined result is just to store as many bits as will fit,
which on a two's complement machine would produce -1. Meanwhile,
`unsigned char' *is* able to hold 255, so your interesting observation
is that 255 != -1; most people find this unsurprising.
 
B

Ben Bacarisse

Does anyone care to comment on the below program I wrote?

First off, it's a C++ program and not a C one. I don't think there is a
significant area of difference in this program, but if you want answers
about C++ then comp.lang.c++ is a better place. It's possible that
follow-ups might stray into and area where the is an important
difference between the two languages.
The question I was wondering is: why do compilers still use IMUL or
MUL for signed or unsigned multiplication respectively when there is
no difference in the numeric value of the result provided the product
size is the same as the size of the two factors that are being
multiplied together?

This is not really a C (or C++) question. You give a reasonable
explanation below, but from a C point of view I wonder why you care. Do
you think the compiler is getting something wrong as a result of the
choice being made?
This program prints PASS on my screen when I run it. Am I missing
something?

Since you ask, the answer must be yes. Presumably you expected
something else, but I can't be sure what. People here could offer more
help if you say why you think you might be missing something.
Here is what I think is going on. The Intel manual lists IMUL for
signed imultiplication and MUL for unsigned. It does this because
originally those instructions always doubled capacity, i.e. the
product had twice the size of the factors.

But then a variant of IMUL was introduced that discards the upper half
of the result, i.e. it works like the C * operator. This is still
listed as being for "signed" multiplication but it works equally well
for unsigned multiplication and might be more efficient because it can
take more operands.

Do any compiler writers know about this?? Or do they blindly trust the
Intel manuals?

I would image that they trust the hardware documentation, but not
"blindly". I feel I'm not really getting at what is bothering you.
(By the way--interestingly I can't get (unsigned char)(0xff) to equal
(signed char)(0xff) in GCC).

Well, there's something wrong with your expectation here. Whilst
it's possible for these to be equal on some systems, it is not "normal"
for the systems that have gcc ported to them.
#include <cstdio>

Change to stdio.h to make this a standard C header.
int main()

In C, int main(void) is (marginally) better.
{
for(int a = 0; a < 256; ++a)
{
for(int b = 0; b < 256; ++b)
{
signed char sa = a;
signed char sb = b;
unsigned char ua = a;
unsigned char ub = b;
signed char sc = sa * sb;
unsigned char uc = ua * ub;
unsigned char usc = sc;
if(uc != usc)
{
std::printf("FAIL %02x * %02x = %02x vs. %02x\n", ua, ub, (unsigned int)(unsigned char)(sc), uc);
break;
}
}
}
std::printf("PASS\n");
return 0;
}

Turn std::printf to plain printf to make this C.
 
W

wrschlanger

Hi Ben,

I do a lot of low level stuff and regularly look at the output from my C compiler. (My apologies for posting C++ code to a C newsgroup).

I was basically wondering about Visual C++ whose code seemed (at least for the old version I checked) to always use MUL for 32-bit unsigned multiplication, and I was wondering whether or not my observation that IMUL would work just as well, was accurate.

This was answered by an earlier post, though I don't have a proof, I guess I don't need one as long as there's a general consensus -- and the fact that GCC generates identical code (using IMUL) is enough proof for me.

The reason I made the post was because I was unaware that GCC did this.

On Sunday, June 17, 2012 8:37:37 AM UTC-7, Ben Bacarisse wrote:
[snip]
Turn std::printf to plain printf to make this C.

Thanks for the tips on converting to C. If I have any other questions I will use a C++ newsgroup, or make sure everything works with plain C. Actuallythis question would have been better suited for an assembly group since it's specific to x86, but then they might complain I used C and not assembly :)


Willow
 
B

BartC

I was basically wondering about Visual C++ whose code seemed (at least for
the old version I checked) to always use MUL for 32-bit unsigned
multiplication, and I was wondering whether or not my observation that
IMUL would work just as well, was accurate.

This was answered by an earlier post, though I don't have a proof, I guess
I don't need one as long as there's a general consensus -- and the fact
that GCC generates identical code (using IMUL) is enough proof for me.

I'm doing some code generation at the moment and using MUL and IMUL. If the
latter works just as well, and could be faster because of the 32-bit, rather
than 64-bit, result, then I will use it.

Although the brief test I've just done shows no difference (not on my
processor anyway).

As for proof, testing that I get the same results on a selected range of
values will do me. It's not necessary to test all 2**64 possible pairs of
operands (and which would anyway take too long). And I wouldn't rely on the
output of GCC; there could be many reasons why the generated code is what it
is.
 
H

Heikki Kallasjoki

....
operands (and which would anyway take too long). And I wouldn't rely on the
output of GCC; there could be many reasons why the generated code is what it
is.

The (maligned) Intel manual[1] is actually reasonably clear about this,
if you don't mind trusting it "blindly" for this occasion:

"The two- and three-operand forms may also be used with unsigned
operands because the lower half of the product is the same regardless if
the operands are signed or unsigned. The CF and OF flags, however,
cannot be used to determine if the upper half of the result is
non-zero."

(When using the "correct" version of MUL/IMUL, the CF or OF flag -- they
have the same value -- can be tested to see if any significant bits will
be lost when truncating the result back to 32 bits, in case this is of
interest.)


[1] Intel(R) 64 and IA-32 Architectures Software Developer's Manual,
Volume 2 (2A, 2B & 2C): Instruction Set Reference, A-Z: "IMUL--Signed
Multiply", Vol. 2A, pp. 3-495.
 
J

Joe.

Ben Bacarisse said:
First off, it's a C++ program and not a C one.

But you experience has no value.?

It's not about programming anymore, it's about who you programmed for. ?

I ask the hard questions. I should just shut up and capitalize on them?

Did Jesus die for anyone's sins?
 
J

Joe.

Hi Ben,

"I do a lot of low level stuff and regularly look at the output from my C
compiler. (My apologies for posting C++ code to a C newsgroup)."

But you don't want one of "dem jobs" either, huh? Did "your country" fail
you?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top