Help with preprocessor math

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Help with preprocessor math

7,781 Views
dwhite
Contributor I
I have the following macro:
#define D_GAIN  ((32 * 16 * 1000 )/ (50 * 16))
When I use it, I always typecast for whatever size I am going into like:
gain = (uWord)D_GAIN;
 
but it refuses to pre-process correctly. In this case, it should be 640 but it comes out as 65521. It appears that it wants me to typecast to prevent overflow when it multiplies 32 * 16 * 1000. If I do it like this, it works....
#define D_GAIN  (((uLong)32 * (uLong)16 * (uLong)1000 )/ (uLong)(50 * 16))
This suggests that the preprocessor is trying to do this math as a 16 bit number. Shouldn't the preprocessor use floating point math, if necessary for the numbers in #defines up until they are needed by the compiler? Is there some compiler options setting that turns off floating point math in the preprocessor?
 
-Dan
Labels (1)
0 Kudos
6 Replies

874 Views
dwhite
Contributor I
I have written code for 32bit and 8bit targets but no 16 bit microcontrollers before but the size of the target int shouldn't have anything to do with this problem.
 
Ok...I may have the technical terminology wrong but I still don't think you understand my problem. I am NOT talking about run-time math on my 16 bit CPU here but compile time math. I want the compiler to correctly perform the math and reduce the equation down to a single 16 bit number to be stored in FLASH. I have now solved this problem by changing one of my constants by adding a ".0" to the end. This apparently indicates the need to use floating point math and correctly gives me the const value of 416...
 
#define D_GAIN ((32.0 * 16 * 1000) /(50 * 16))
 
EXTERN const uWord D_Gain (uWord)(0.651 * D_GAIN);
 
I just have to go through all of my code and add ".0" to the end of a bunch of values.
0 Kudos

874 Views
CompilerGuru
NXP Employee
NXP Employee
>but the size of the target int shouldn't
>have anything to do with this problem.

Quite the opposite. It's almost the only thing which matters.
As long as the single numbers do fit into a int, then the computation have to be done as int only. This is defined by ANSI-C, and the compiler is not free to do it "better", to compute intermediate values as long.

I'm not sure this .0 suffix is such a good idea. Basically it causes that all the computations will be performed as double. And therefore there is a high likelyhood that some computations will use floating point math at runtime too. Not the one using "D_Gain", but there may be others directly using D_GAIN.

So I would really recommend to use the long syntax with the L suffix to perform all arithmetic in longs (32 bit) if you have such 32 bit intermediate results.

Also, using doubles (the .0 suffix) is not the same thing as using integral values. For your particular case you get the same value as the division results in an integral value, but in general the integral division will truncate and the floating point division will return the floating point approximation.

Did you check the link to the ANSI-C FAQ I had in my pref post? I think it is important to understand the traps of the language one is programming with.
0 Kudos

874 Views
michaelh
Contributor I
I think the problem is making things consistent. If you have this expression:

int foo = 5 * 6 * 7;

then the compiler has the option of rolling this up into a 'foo = 210' or being a bit dumb and doing the multiplications at runtime. To keep things consistent then both have to get the same result, so if you get an overflow doing it the hard way you should also get an overflow when the compiler optimises things.

The short answer is you have to get the same answer optimised or not so the compiler has to follow the same rules and integer size as the target.
0 Kudos

874 Views
CrasyCat
Specialist III

Hello

The ANSI C standard tells that arithmetic on constants is perform on an int (16-bit for HC08 or HC12 compiler) when the constant can be encoded on an int (Which is the case here).

You have to cast one of the operator (usually the first one) to get it done on 32-bit.

Note that ANSI C provides an alternate way to tell the compiler to interpret an integer constant as a long.

Writing your macro as follows will ensure the preprocessor evaluates it as 640:

#define D_GAIN  ((32L * 16 * 1000 )/ (50 * 16))

The L stands for long. You can also use UL for unsigned long.

I hope this helps.

CrasyCat

0 Kudos

874 Views
dwhite
Contributor I

Apparently, no compiler I have ever used (and that's at least 10) has been compliant with ANSI C because I have never run across this before. Is there a way I can turn this feature off because I have a LOT of legacy code here that won't build correctly without this modification and loss of portability. If not, how would I get it to use floating point for things like this...

#define D_GAIN  ((32 * 16 * 1000 )/ (50 * 16))

EXTERN const uWord D_Gain (uWord)(0.651 * D_GAIN);

In this case, I expect the preprocessor to use floating point and then truncate the value to 16 bits resulting in a const value of 416. Do you know where I can find this kind of information in the Codewarrior docs because I can't find it anywhere?

 

0 Kudos

874 Views
CompilerGuru
NXP Employee
NXP Employee
Did you code for a 16 bit int target before?

You double wont actually help.
>#define D_GAIN ((32 * 16 * 1000 )/ (50 * 16))
>EXTERN const uWord D_Gain (uWord)(0.651 * D_GAIN);

In ANSI-C, the way an expression is used does not influence how it is computed, so your D_GAIN macro has the same problem as before. If you want to use long arithmetic, you have to have long operand in the multiplication which overflows. And the simplest way is to add a L suffix as crazycat suggested.
See also:
http://c-faq.com/expr/intoverflow1.html


Daniel

PS: In technical terminology, the thread title is actually wrong as not the preprocessor does the math in your case. The preprocessor does it differently (has to, to follow ANSI-C).
0 Kudos