20140615

Do you see too?

Here's another gripe about the 'C' programming language or at least Microchip's C18 implementation of it. Oh, and by the way, here's another programmer's take on the subject.

Here a quote from the C18 user guide:

2.7.1 Integer Promotions
ISO mandates that all arithmetic be performed at int precision or greater. By default, MPLAB C18 will perform arithmetic at the size of the largest operand, even if both operands are smaller than an int. The ISO mandated behavior can be instated via the -Oi command-line option.

When coding for an 8-bit microprocessor one tends to use 8-bit (byte) variables wherever possible as these are native to the processor.  If you are coding in C18 you are likely to be using one of the PIC18 range of processors and these, we know, have a hardware 8-bit by 8-bit multiplier giving a 16-bit result in one machine cycle.

unsigned char a, b;
unsigned short int c;
a = 100;
b = 200;
c = a * b;

How crazy is it, then, that the above code in C18 will yield 'c' = 32!  the last line will of course use the hardware multiplier and then will throw away the upper byte of the result before expanding the answer to 16-bits by adding a zero upper byte and placing it in variable 'c'.

Worse still is

#define factor 31
unsigned char a;
a = ((factor * 60) / 100);

Here the intention is to give variable 'a' the value 60% of that of constant 'factor'. The constant expression to the right of the equals sign is, of course, evaluated at compilation time so that the run-time code sees simply 'a' being set to a constant.  In assembler I am used such expressions being evaluated without loss of precision until the answer is placed into 'a' and raising a warning if it is too big. But in C18 the expression is evaluated in byte precision and yielding 'a' = 0. To stop this happening one can use:

a = ((factor * 60ul) / 100ul);
to force the evaluation to use unsigned long (32-bit) precision, but this is hardly intuitive.  I would not have so much minded had C18 deigned to raise a warning but, no, it blithely continues in its legalistic, archaic and ridiculous way...

The reason for using 'C' for embedded processors is to increase productivity and yet produce tight, efficient code. The above example shows how 'C' fails to do this as, I think, the only way to force the compiler to do the obvious is to insert assembler code.

No comments:

Post a Comment