C中arcsin的逼近

时间:2013-11-23 10:45:06

标签: c algorithm precision trigonometry taylor-series

我有一个程序可以根据泰勒系列计算arcsin值的近似值。

arcsin

我和我的朋友想出了一个算法,该算法能够返回差不多的"右键"价值观,但我不认为我们已经非常清晰地完成了它。看看:

double my_asin(double x)
{
    double a = 0;
    int i = 0;
    double sum = 0;
    a = x;
    for(i = 1; i < 23500; i++)
    {
        sum += a;
        a = next(a, x, i);
    }
}

double next(double a, double x, int i)
{
    return a*((my_pow(2*i-1, 2)) / ((2*i)*(2*i+1)*my_pow(x, 2)));
}

我检查了my_pow是否正常工作,所以我也不需要在这里发布它。基本上我想要在当前和下一个术语之间的差异大于或等于我的EPSILON(0.00001)时结束循环,这是我在计算平方根时所使用的精度。


这就是我希望它的工作方式:

while(my_abs(prev_term - next_term) >= EPSILON)

但是函数 double next 依赖于 i ,所以我想我也必须在while语句中增加它。有什么想法我应该怎么做呢?


-1的输出示例:

$ -1.5675516116e+00

而不是:

$ -1.5707963268e+00

非常感谢你们。

4 个答案:

答案 0 :(得分:3)

您的代码和问题的问题包括:

  1. 显示arcsin的泰勒级数的图像文件有两个错误: x 5 术语上有一个减号,而不是加号,并且 x 显示为 x n 但应 x 2 < i>名词的1
  2. arcsin 的泰勒级数中 x 因子在每个项中增加 x 2 ,但您的公式a*((my_pow(2*i-1, 2)) / ((2*i)*(2*i+1)*my_pow(x, 2))) 在每个字词中按 x 2 划分。这与您询问的特定值-1无关,但除了1之外,它会对其他值产生错误的结果。
  3. 一旦术语的差异“大于或等于”你的epsilon,你就会问如何结束循环,但是,对于 x 的大多数值,你实际上想要小于(或者相反,您希望继续,而不是结束,而差异大于或等于,如您在代码中所示。)
  4. 泰勒级数是评估函数的一种不好的方法,因为当你从系列居中的位置越来越远时,它的误差会增加。像这样的函数的大多数数学库实现使用minimax系列或与之相关的东西。
  5. 从低阶项到高阶项评估系列会导致您先添加较大的值,然后再添加较小的值。由于浮点运算的本质,这意味着较小项的精度会丢失,因为它被较大的值“推出”浮点格式的宽度。此效果将限制任何结果的准确程度。
  6. 最后,为了直接解决您的问题,您构建代码的方式,您直接更新a,这样您就不会同时拥有上一个词和下一个词。而是创建另一个double b,以便您拥有前一个术语的对象b和当前术语的对象a,如下所示。
  7. 示例:

    double a = x, b, sum = a;
    int i = 0;
    do
    {
        b = a;
        a = next(a, x, ++i);
        sum += a;
    } while (abs(b-a) > threshold);
    

答案 1 :(得分:1)

  

所以我想我也必须在while语句中增加它

是的,这可能是一种方式。是什么阻止了你?

int i=0;
while(condition){
   //do something
   i++;
}

另一种方法是使用for条件:

for(i = 1; i < 23500 && my_abs(prev_term - next_term) >= EPSILON; i++)

答案 2 :(得分:1)

你的公式错了。这是正确的公式:http://scipp.ucsc.edu/~haber/ph116A/taylor11.pdf

P.S。另请注意,您的公式和系列彼此不对应。

enter image description here


您可以像这样使用:

while( std::abs(sum_prev - sum) < 1e-15 )
    {
        sum_prev = sum;
        sum += a;
        a = next(a, x, i);
    }

答案 3 :(得分:1)

对于arcsin使用泰勒系列是非常不精确的,因为这些东西收敛得非常糟糕,并且对于有限数量的温度,真实的东西会有相对较大的差异。使用带有整数指数的pow也不是非常精确和有效。

然而,使用arctan就可以了

arcsin(x) = arctan(x/sqrt(1-(x*x)));

因为它的泰勒级数在<0.0,0.8>范围内收敛,所以该范围的所有其他部分都可以通过它计算(使用三角恒等式)。所以我的 C ++ 实现(来自我的算术模板):

T atan    (const T &x)                                              // = atan(x)
    {
    bool _shift=false;
    bool _invert=false;
    bool _negative=false;
    T z,dz,x1,x2,a,b; int i;
    x1=x; if (x1<0.0) { _negative=true; x1=-x1; }
    if (x1>1.0) { _invert=true; x1=1.0/x1; }
    if (x1>0.7) { _shift=true; b=::sqrt(3.0)/3.0; x1=(x1-b)/(1.0+(x1*b)); }
    x2=x1*x1;
    for (z=x1,a=x1,b=1,i=1;i<1000;i++)  // if x1>0.8 convergence is slow
        {
        a*=x2; b+=2; dz=a/b; z-=dz;
        a*=x2; b+=2; dz=a/b; z+=dz;
        if (::abs(dz)<zero) break;
        }
    if (_shift) z+=pi/6.0;
    if (_invert) z=0.5*pi-z;
    if (_negative) z=-z;
    return z;
    }
T asin    (const T &x)                                              // = asin(x)
    {
    if (x<=-1.0) return -0.5*pi;
    if (x>=+1.0) return +0.5*pi;
    return ::atan(x/::sqrt(1.0-(x*x)));
    }

T是任何浮点类型(float,double,...)。如您所见,您需要执行sqrt(x)pi=3.141592653589793238462643383279502884197169399375105zero=1e-20+,-,*,/操作。 zero常量是目标精度。

所以只需将T替换为float/double,然后忽略:: ...