views:

60

answers:

3

I have the following code, for an embedded platform where an int is 16 bits and a long int is 32 bits:

#define MULTIPLIER 0x1000

static void my_function(uint16_t i, void *p)
{
    uint32_t start = MULTIPLIER * i;
    ...
}

My compiler gives me the warning:

Warning 1 : lower precision in wider context: '*'

for this line.

What does this really mean? I can make the warning go away by changing the #define to

#define MULTIPLER 0x1000ul

(explicitly making it an unsigned long) but I would like to understand the warning.

+2  A: 

I think the warning means that you probably want a 32-bit result, but since 0x1000 has type int and i has type int16_t, the expression is only type int and likely to overflow and not give you the results you want.

R..
+3  A: 

It's warning you that the multiplication will take place using 16-bit values, and the 16-bit result will then be converted to a 32-bit result. This might not be what you expect (the 16-bit multiplication might overflow), hence the warning.

This forum posting covers the issue

David Gelhar
While this sounds plausible I believe you are mistaken. I'm pretty sure the multiplication is done in 32 bit registers (on i386) regardless of the 16 bit declaration. The result will be placed in a 32 bit target so no truncation. I think the issue is the signed vs unsigned conflict but I'd have to see asm code to be sure.PS: I don't think this is an overflow warning since that's a runtime thing. 16 bit * 16 bit may or may not overflow depending on operand values.
Hotei
@Hotei: You're wrong for multiple reasons. (1) this is not i386 but some unspecified 16-bit embedded environment. and (2) C **requires** the result to be truncated to 16 bits in this case.
R..
One major peeve I had with Macintosh C compilers was that even though the 68000 only has a 16x16->32 multiply instruction, there was no nice way to code 16x16->32 multiplication in C. The compiler would go out of its way to truncate the result of the multiply instruction unless one of the operands was cast to long, in which case it would waste time doing a long*long multiplication. On 8-bit embedded systems, I sometimes use my own multiply and divide routines; my favorite is ldivmod, which divides a global long by an 8-bit value for an 8-bit result.
supercat
@R..: What I'm saying is that C (IIRC) converts operands to the widest of its operands, ie, 16 bit * 32 bit would be done at 32 bit precision.If we're assuming a "recent" implementation of C, then #define MULTIPLIER 0x1000 would be interpreted as int, ie 32 bit signed. Stuffing the result of unsigned 16 bit and signed 32 bit into an unsigned 32 bit will be (undefined) which is what the compiler is trying to suggest with the admittedly ambiguous error message. Like I said, I'd need to see the asm to see if that's what is really happening. Implementations vary widely.
Hotei
continued - tried to edit but it locked the comment. replace "be (undefined)" with "lose the sign bit". What the compiler can't do is convert 32signed to 32 unsigned without loss of "Precision" which is perhaps a poor choice of wording for losing 1/2 the range of 32 bit integers.
Hotei
@Hotei: Re-read the question. `int` is 16-bit on this platform so both operands are 16-bit.
R..
Ok, 16 bit it is, but there is still a sign bit missing. 16 bit signed * 16 bit unsigned stored into an unsigned anything is going to potentially drop a sign bit. The compiler apparently calls this "loss of precision". Nothing to do with "overflow". Like I said, poor choice of error message.
Hotei
+1  A: 

For the expression MULTIPLIER * i the compiler chooses a type of greater precision from types of operands. In the first case you give it two ints and that means you will have a truncated result, that will afterwards be converted to long int, but the precision will be still 16 bits.

in the latter case you explicitly make one of your operands long, and the result is then written to a variable of the same precision.

ULysses