我有#define
预定义的2度到弧度函数:
功能1:
#define degreesToRadians(degrees) (M_PI * degrees / 180.0)
功能2:
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
只有第二个函数返回正确的答案,而第一个函数提供了奇怪的答案。它们之间有什么区别?
答案 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;
}