这两个功能之间的区别?

时间:2015-02-18 07:01:27

标签: objective-c

我有#define预定义的2度到弧度函数:

功能1:

#define degreesToRadians(degrees) (M_PI * degrees / 180.0)

功能2:

#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)

只有第二个函数返回正确的答案,而第一个函数提供了奇怪的答案。它们之间有什么区别?

2 个答案:

答案 0 :(得分:3)

上面提到的两个“函数”中没有一个是函数,它们是宏,第一个宏不安全,例如,扩展宏degreesToRadians(10 + 10)给出(M_PI * 10 + 10 / 180.0),这被解释为((M_PI * 10) + (10 / 180.0))这显然是错误的。虽然展开DEGREES_TO_RADIANS(10 + 10)会给((10 + 10 ) / 180.0 * M_PI)这是正确的。

另一个区别是M_PI * degreess可能会溢出双边界,从而给出错误的答案(但这需要degrees中相当高的值)

答案 1 :(得分:2)

计算几乎完全相同,尽管存在浮点限制。但是,您在第二个宏中用括号括起angle,这是正确的要做的事情。

第一个宏中,如果您这样做:

x = degreesToRadians(a + 45);

然后,记住宏是简单的文本替换,你最终会得到:

x = (M_PI * a + 45 / 180.0);

结束,因为它将被计算为好像你写的:

x = (M_PI * a) + (45 / 180.0);

换句话说,您只需将角度乘以PI并添加常量0.25

如果您将第一个更改为:

#define degreesToRadians(degrees) (M_PI * (degrees) / 180.0)
然后它应该开始表现得更好。


另一个区别与角度的大或小值有关。然后在一个小角度上进行除 - 然后乘法(我的意思是真正小,如10-308,接近IEEE754限制)可能导致零结果,而乘以 - 然后除以角度(如10308)可能会让你溢出。

我的建议是确保您使用“正常”角度(或在转换前将其标准化)。如果你这样做,每种方法的不同边缘条件都无关紧要。


而且,老实说,你甚至可能不应该使用宏。通过疯狂地优化编译器和枚举,宏现在几乎应该降级为条件编译。我只是将它重写为一个函数:

double degreesToRadians(double d) {
    return M_PI * d / 180.0;
}

或者,你甚至可以调整代码,以便担心小角度或大角度(如果你是偏执狂):

double degreesToRadians(double d) {
    if ((d > -1) && (d < 1))
        return (M_PI * d) / 180.0;
    return (d / 180.0) * M_PI;
}