使用宏进行错误的转换

时间:2017-05-12 17:14:47

标签: c++ c macros c-preprocessor

所以我有一个奇怪的问题,GNU GCC(C / C ++)宏定义如下:

#define PI                  3.14159265359
#define DEG_TO_RAD(a)       (a * PI / 180.0)
#define ARCSEC_TO_DEG(a)    (a / 3600.0)
#define ARCSEC_TO_RAD(a)    DEG_TO_RAD( ARCSEC_TO_DEG( a ) )

正如您所知,宏只是将弧的秒数转换为弧度。但是,根据宏的应用位置,我得到了不同的结果:

double xi2 = ARCSEC_TO_RAD(  2306.2181 * c + 0.30188 * c2 + 0.017998 * c3);
double xi = 2306.2181 * c + 0.30188 * c2 + 0.017998 * c3; 

printf("c = %.10f; xi = %.10f = %.10f = %.10f; ", 
       c, xi, ARCSEC_TO_RAD(xi), xi2);

输出:

c = 0.1899931554; xi = 438.1766743152 = 0.0021243405 = 7.6476237313;

哪里是愚蠢的错误......?

2 个答案:

答案 0 :(得分:3)

我强烈建议你使用函数(也许是inline)而不是MACROS, 但如果由于某种原因你不能解决问题,那么解决方法可能是在收到的参数中添加括号:

#define PI                  3.14159265359
#define DEG_TO_RAD(a)       ((a) * PI / 180.0)
#define ARCSEC_TO_DEG(a)    ((a) / 3600.0)
#define ARCSEC_TO_RAD(a)    DEG_TO_RAD( ARCSEC_TO_DEG( (a) ) )  
//In the lastone () is not necessary but it a good prectice always adding parenthesis to macro args

这可以防止在扩展宏时出现与运算符优先级相关的错误。

答案 1 :(得分:2)

一步一步走,

ARCSEC_TO_RAD(  2306.2181 * c + 0.30188 * c2 + 0.017998 * c3);

将扩展为

DEG_TO_RAD( ARCSEC_TO_DEG(2306.2181 * c + 0.30188 * c2 + 0.017998 * c3))
DEG_TO_RAD( (2306.2181 * c + 0.30188 * c2 + 0.017998 * c3 / 3600.0))
((2306.2181 * c + 0.30188 * c2 + 0.017998 * c3 / 3600.0) * P* / 180.0)

现在常规操作顺序就在这里,因此2306.2181 * c + 0.30188 * c2 + 0.017998 * c3 除以3600.只有0.017998 * c3会。旧学校C解决方案是在宏观替换周围放置括号。

现代的C和C ++解决方案是使用功能。 inline函数need to to meet ODR,但编译器可能会自行决定是否应该内联扩展函数。

这个问题标记为C ++,所以这里是C ++解决方案:

#include <iostream>

constexpr double PI = 3.14159265359;
/* or
#include <cmath>
const double PI = std::acos(-1);
but I'm not certain you can properly constexpr this */

double DEG_TO_RAD(double a)
{
    return a * PI / 180.0;
}
double ARCSEC_TO_DEG(double a)
{
    return a / 3600.0;
}
double ARCSEC_TO_RAD(double a)
{
    return DEG_TO_RAD( ARCSEC_TO_DEG( a ) );
}

int main ()
{
    double c = 10;
    double c2 = 20;
    double c3 = 30;

    std::cout << DEG_TO_RAD(2306.2181 * c + 0.30188 * c2 + 0.017998 * c3) << std::endl;
}

在C ++ 11或更新版本中,constexpr can be added使这些以前的宏编译时常量应该是必要的。