views:

76

answers:

2

Hello, I can't understand why does my program do such calculations:

#define PixelsPerMeter 40.0

// 40 pixels ~ 1 meter
#define Meters2Pixels(meters) (float)meters*PixelsPerMeter
#define Pixels2Meters(pixels) (float)pixels/PixelsPerMeter

// The speed of free falling
#define G Meters2Pixels(9.81)

// ...
float mHeight = 768;
float _windPower = Meters2Pixels(-5.0);
// ...

float x1 = ( mHeight / G ) * _windPower;

cout << "G: " << G << "; wind: " << _windPower << "\n";
cout << "Height: " << mHeight << "\n";
cout << "Calculating: " << mHeight / G * _windPower << "\n";

=>
G: 392.4; wind: -200
Height: 768
Calculating: -626300

I can't understand why... If I calculate this by hands, for example, I have to get: -391.4

What's wrong?

+4  A: 

You need to enclose the macro expansions in parenthesis as:

#define Meters2Pixels(meters) ((float)meters*PixelsPerMeter)
#define Pixels2Meters(pixels) ((float)pixels/PixelsPerMeter)

The C/C++ preprocessor does a blind substitution. Without the parenthesis, x1 will be computed as:

float x1 = ( mHeight / (float)9.81*40.0 ) * _windPower;

which divides mHeight by 9.81 and then multiplies the result with 40.0. Which is not you want. You want mHeight to be divided by the product of 9.81 and 40.0 hence the parenthesis.

you can always see how your macros get expanded before your file goes for compilation by using the -E option of g++ as:

g++ -Wall -E myfile.cpp 

EDIT:

Its also best to enclose the macro parameter in parenthesis as well, this will guard you if you pass an expression ( like say Meters2Pixels(foo + 2):

#define Meters2Pixels(meters) ((float)(meters)*PixelsPerMeter)
#define Pixels2Meters(pixels) ((float)(pixels)/PixelsPerMeter)
codaddict
I'd also recommend enclosing the macro arguments in parenthesis as well, in case you pass in a value that changes your operator precedence to something undesirable.
mdec
Great answer, thanks. Yeah, macros are evil :)
Ockonal
Macros aren't evil, but I don't think this is a great use case.
mathepic
+3  A: 

To avoid trouble with macros, you should consider using inline functions.

ninjalj