1

I have this simple code that converts between degrees Farehnheit and degrees Celcius. I defined some macros to do it, but I'm getting some weird results when I use it. I use this method when absoluteTemp = 373.15 (the boiling point of water in kelvin).

#define kelvinToCelc(k) k - 273.15
#define celcToFahren(c) (9.0 / 5.0) * c + 32

double x = kelvinToCelc(absoluteTemp);              // 100
double y = celcToFahren(x);                          // 212
double z = celcToFahren(kelvinToCelc(absoluteTemp)); // 430.52???
return celcToFaren(kelvinToCelc(absoluteTemp));
michaelsnowden
  • 6,031
  • 2
  • 38
  • 83

7 Answers7

4

After expanding macro in

double z = celcToFahren(kelvinToCelc(absoluteTemp));

It becomes

double z = (9.0 / 5.0) * absoluteTemp - 273.15 + 32

You need to add parentheses to the macros

#define kelvinToCelc(k) (k - 273.15)
#define celcToFahren(c) ((9.0 / 5.0) * c + 32)
Jeremy
  • 4,797
  • 1
  • 18
  • 26
  • 2
    And the second. Just imagine if `c` is `x + y`. – chris Jan 16 '14 at 04:01
  • 1
    You probably should add paranthesis to all the macros to avoid precedence screwing up your math. –  Jan 16 '14 at 04:02
  • I accepted your answer because it offered a good explanation and solution – michaelsnowden Jan 16 '14 at 04:36
  • @doctordoder - not to repeat my answer but, you should also put parens around the `k` and `c` if you must use macros, but really you should use inline functions and avoid the whole mess – Glenn Teitelbaum Jan 16 '14 at 06:16
  • @GlennTeitelbaum Is there anything you want to add to [this answer](http://stackoverflow.com/a/1137654/2770572) on another thread about inline functions vs macros? The concept is new to me, and I'd appreciate some direction. It seems simple to me, in the same place I'd put define, just make a simple function there instead with `inline` in front of it. – michaelsnowden Jan 16 '14 at 06:28
  • @doctordoder take a look at my answer, I editted to show you what the transformation will look like for your macros – Glenn Teitelbaum Jan 16 '14 at 06:37
2

The old rule was : Use more parenthesis in macros around everything:

#define kelvinToCelc(k) ((k) - 273.15)
#define celcToFahren(c) ((9.0 / 5.0) * (c) + 32)

Notice parens around the whole macro and all macro arguments

The new rule is : Use inline functions They have typechecking, evaluate arguments only once, and because they don't need so many parenthesis *

Note: * Some exceptions may apply, this is not one of them

This is what that looks like as inline functions

inline double kelvinToCelc(double k)
{
    return k - 273.15;
}

inline double kelvinToCelc(double c)
{
    return (9.0 / 5.0) * c + 32;
}

Notice that you have to put inline and the return type before the name, add types to all arguments, and add a ; at the end

Notice how you can use newlines to make it easier to read, and also so you can step into it in the debugger

Glenn Teitelbaum
  • 10,108
  • 3
  • 36
  • 80
1

Macros are simple - Just a text replacement

i.e.

 double z = celcToFahren(kelvinToCelc(absoluteTemp)); 

becomes

 double z = (9.0 / 5.0) * kelvinToCelc(absoluteTemp) + 32

Then becomes

 double z = (9.0 / 5.0) * absoluteTemp - 273.15  + 32

Now just do the maths

i.e.

double z = (9.0 / 5.0) * 373.15 - 273.15  + 32;
Ed Heal
  • 59,252
  • 17
  • 87
  • 127
  • You say macros are simple. I sentence you to an hour of staring at the Boost.Preprocessor implementation (or quite a few other libraries, even with those amazing utilities). – chris Jan 16 '14 at 04:05
  • @chris - I find them simple - Just use a pencil and a rubber – Ed Heal Jan 16 '14 at 04:07
1

If you are going to use macros, make your life easier with parenthesis

#define kelvinToCelc(k) (k) - 273.15
#define celcToFahren(c) (9.0 / 5.0) * (c) + 32 

This helps prevent the unexpected results you are seeing. The reasons have already been pointed out in other posts

Dweeberly
  • 4,668
  • 2
  • 22
  • 41
0

For macros, it just relies on textual substitution. So it is equivalent to:

double z = (9.0 / 5.0) * absoluteTemp - 273.15 + 32;

That's why you got wrong result.

Edit:

  1. Try to use (inline) functions instead even you can make it work by adding more parenthesis: (see #2)

    #define celcToFahren(c) ((9.0 / 5.0) * (c) + 32)
    
  2. Macros are also error-prone because they rely on textual substitution and do not perform type-checking. Check out here for more info.

Community
  • 1
  • 1
herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
  • Edit 1 isn't a very good solution. `celcToFahren(x) * 5` for example would result in unwanted behaviour again (the `32` would be multiplied by `5`). –  Jan 16 '14 at 04:09
  • @user2176127 Fixed by adding MORE parenthesis. Thanks. – herohuyongtao Jan 16 '14 at 04:11
  • It seems Lisp is just C's arithmetic macro idioms turned into a language. – chris Jan 16 '14 at 04:12
0

Just one thing i should add to the other answers, try running just the preprocessor and look at the output ie: g++ -E -P main.cpp

da_steve101
  • 283
  • 4
  • 16
0

The answer 431.52 is correct. It expanded like this

(9.0/5.0) * 373.15 - 272.15 + 32

In mathematics [* and /] take precedence over [+ and -]. So the equation expanded like

((9.0/5.0) * 373.15) - 272.15 + 32
(671.67) - 272.15 + 32
399.52 + 32
431.52

[* and /] have same precedence so there order doesn't matter and similarly [+ and -] have same precedence so their execution order doesn't matter.

Vikram Singh
  • 948
  • 6
  • 10