在没有数学库的情况下逼近 atan
Approximating atan without a math library
我一直在谷歌上搜索这个问题的解决方案。我见过多种计算任何 -1 <= theta <= 1 的 atan(theta) 的方法,但我不确定当 theta 大于或小于这些界限时该怎么做。
我假设我需要加上或减去圆周率的倍数来抵消它?这个思路对吗?
目前我有:
double my_atan(double x)
{
return x - (x*x*x)/3 + (x*x*x*x*x)/5;
}
正在使用 taylor series。
对于下面的代码,
int x;
for (x=0; x<M_PI*2; x++)
{
printf("Actual: %f\n", atan(x));
printf("Approx: %f\n", my_atan(x));
printf("\n");
}
它很快失去控制(正如预期的那样,因为它超出了范围):
Actual: 0.000000
Approx: 0.000000
Actual: 0.785398
Approx: 0.866667
Actual: 1.107149
Approx: 5.733333
Actual: 1.249046
Approx: 42.600000
Actual: 1.325818
Approx: 187.466667
Actual: 1.373401
Approx: 588.333333
Actual: 1.405648
Approx: 1489.200000
此处未显示,但当 theta 在适当范围内时,输出相当准确。
所以我的问题是 my_tan 函数究竟需要采取哪些步骤才能支持更宽的范围?
已经关注了一段时间,所以如果能提供任何指导,我们将不胜感激
让我完成您的示例并讨论一些可能有帮助的事情:
#include <stdio.h>
#include <math.h>
double my_atan(double x)
{
return x - (x*x*x)/3 + (x*x*x*x*x)/5 - (x*x*x*x*x*x*x)/7;
}
int main()
{
double x;
for (x=0.0; x<M_PI*2; x+=0.1)
{
printf("x: %f\n", x);
printf("Actual: %f\n", atan(x));
printf("Approx: %f\n", my_atan(x));
printf("\n");
}
}
项 int x
是一个整数,您正在逼近大角度。尝试在这里使用双打你不会得到转换错误。
现在解决你的问题。您使用的泰勒级数仅在您的 |x| 时有效< 1.
泰勒级数越远离给定点或在您的情况下为零 (0+x)。
该系列在 pi/4
之前工作良好,即使在那个时候它也非常不准确,但更大的值会变得非常糟糕。所以对于较小的角度,它工作得很好。
我用于定点库的解决方案是使用 Remez algorithm 的极小极大近似。即使在那里,我也对三个范围使用了一组不同的系数:0 到 0.5; 0.5 到 0.75,以及 0.75 到 1。通过这种细分,我能够获得 1 ULP 精度。
然后你需要很好的参数缩减来获得范围内的参数。在我的例子中,我对大于 1 的参数使用了一个很好的倒数函数。以下是恒等式:
atan(-x) == -atan(x)
atan(x) == pi/2 - atan(1/x) // for x > 1
有一个 nice blog post on Taylor vs. Remez approximations here; at that site is also a Remez toolkit 用于查找您需要的系数。
我一直在谷歌上搜索这个问题的解决方案。我见过多种计算任何 -1 <= theta <= 1 的 atan(theta) 的方法,但我不确定当 theta 大于或小于这些界限时该怎么做。
我假设我需要加上或减去圆周率的倍数来抵消它?这个思路对吗?
目前我有:
double my_atan(double x)
{
return x - (x*x*x)/3 + (x*x*x*x*x)/5;
}
正在使用 taylor series。
对于下面的代码,
int x;
for (x=0; x<M_PI*2; x++)
{
printf("Actual: %f\n", atan(x));
printf("Approx: %f\n", my_atan(x));
printf("\n");
}
它很快失去控制(正如预期的那样,因为它超出了范围):
Actual: 0.000000
Approx: 0.000000
Actual: 0.785398
Approx: 0.866667
Actual: 1.107149
Approx: 5.733333
Actual: 1.249046
Approx: 42.600000
Actual: 1.325818
Approx: 187.466667
Actual: 1.373401
Approx: 588.333333
Actual: 1.405648
Approx: 1489.200000
此处未显示,但当 theta 在适当范围内时,输出相当准确。
所以我的问题是 my_tan 函数究竟需要采取哪些步骤才能支持更宽的范围?
已经关注了一段时间,所以如果能提供任何指导,我们将不胜感激
让我完成您的示例并讨论一些可能有帮助的事情:
#include <stdio.h>
#include <math.h>
double my_atan(double x)
{
return x - (x*x*x)/3 + (x*x*x*x*x)/5 - (x*x*x*x*x*x*x)/7;
}
int main()
{
double x;
for (x=0.0; x<M_PI*2; x+=0.1)
{
printf("x: %f\n", x);
printf("Actual: %f\n", atan(x));
printf("Approx: %f\n", my_atan(x));
printf("\n");
}
}
项 int x
是一个整数,您正在逼近大角度。尝试在这里使用双打你不会得到转换错误。
现在解决你的问题。您使用的泰勒级数仅在您的 |x| 时有效< 1.
泰勒级数越远离给定点或在您的情况下为零 (0+x)。
该系列在 pi/4
之前工作良好,即使在那个时候它也非常不准确,但更大的值会变得非常糟糕。所以对于较小的角度,它工作得很好。
我用于定点库的解决方案是使用 Remez algorithm 的极小极大近似。即使在那里,我也对三个范围使用了一组不同的系数:0 到 0.5; 0.5 到 0.75,以及 0.75 到 1。通过这种细分,我能够获得 1 ULP 精度。
然后你需要很好的参数缩减来获得范围内的参数。在我的例子中,我对大于 1 的参数使用了一个很好的倒数函数。以下是恒等式:
atan(-x) == -atan(x)
atan(x) == pi/2 - atan(1/x) // for x > 1
有一个 nice blog post on Taylor vs. Remez approximations here; at that site is also a Remez toolkit 用于查找您需要的系数。