我尝试确定从(n,m)
到(0,0)
的角度。如果没有arctan2
可用,我遇到m
可能为0的问题,这可能导致可能被零除。
解决这个问题的优雅,正确的解决方案是什么?
答案 0 :(得分:2)
不要使用常规象限,使用由线y = +/- x定义的分支点,并使用CORDIC类算法的前两个步骤(例如,按已知角度旋转坐标并跟踪你旋转了多少):
function atan2_substitute(x,y)
{
double angle = 0;
if (x < y)
{ angle = M_PI; x = -x; y = -y; }
// this guarantees that the angle is between -135 and +45 degrees
if (x < -y)
{
angle -= M_PI/2; tmp = x; x = -y; y = tmp;
}
// this guarantees that the angle is between -45 and +45
angle += atan(y/x);
if (angle > M_PI)
angle -= 2*M_PI;
// fails at 0,0; otherwise is accurate over the entire plane
}
这样做的原因是atan()可能更准确地适用于-1和+1之间的比率y / x,而不是大于1的比率。(尽管一个好的atan()算法会认识到这一点,并采取倒数)
答案 1 :(得分:1)
如果atan2不可用,您必须检查除零条件和代码中的所有其他特殊情况。很简单。 atan2上的维基百科条目具有您需要的所有条件。
如果您的目标硬件支持浮点运算的除零异常,您还有另一种选择:
安装一个检查异常原因的低级处理程序,如果它发生了atan分区则修复了这个问题。如果异常很少,这将使您的atan2更快,但它需要低水平的修补并且不可移植。
答案 2 :(得分:1)
使用Taylor系列实现标准arctan(n, m)
并在计算arctan之前执行以下操作:
if (m == 0) {
if (n < 0) return Pi;
return 0;
}
另外几招:
1)如果|m| < |n|
,则交换m, n
,然后计算arctan。最后从Pi/2
2)如果|m|
接近|n|
,则使用半角公式计算半角的arctan
arctan(x) = 2*arctan(x/(1+sqrt(1+x*x)))
,否则,对于这些值,arctan收敛非常缓慢
答案 3 :(得分:0)
定义arctan2
的版本。 C中作为宏的一个例子:
#define atan2(n,m) (m)==0 ? M_PI_2 : atan((n)/(m))
当然,您可以根据n
和m
的符号详细说明找到象限。
答案 4 :(得分:0)
我相信这是使用atan的atan2的正确实现(但不处理无穷大):
float my_atan2(float y, float x)
{
if(x == 0) // might also want to use fabs(x) < 1e-6 or something like that
{
if(y > 0)
return M_PI_2;
else
return -M_PI_2;
}
else if(x > 0)
{
return atan(y/x);
}
else
{
// x < 0
if(y > 0)
return M_PI + atan(y/x);
else
return -M_PI + atan(y/x);
}
}
测试工具:
int main()
{
for(int i = -360; i <= 360; i++)
{
float x = cos(i / 180.0 * M_PI);
float y = sin(i / 180.0 * M_PI);
float good = atan2(y, x);
float mine = my_atan2(y, x);
if(fabs(good - mine) > 1e-6)
{
printf("%d %f %f %f %f\n", i, x, y, good, mine);
}
}
}