使用C计算用于sin的maclaurin系列

时间:2015-03-22 21:59:51

标签: c math floating-point

我编写了一个使用maclaurin系列计算sin的代码,但是当我尝试为大x值计算它并试图通过给出一个大的N阶(总和的长度)来抵消它时 - 最终它会溢出并没有给我正确的结果。这是代码,我想知道是否有一种额外的方法来优化它,因此它适用于大的x值(它已经适用于小x值和非常大的N值)。 这是代码:

long double calcMaclaurinPolynom(double x, int N){
    long double result = 0;
    long double atzeretCounter = 2;
    int sign = 1;
    long double fraction = x;

    for (int i = 0; i <= N; i++)
    {
        result += sign*fraction;
        sign = sign*(-1);
        fraction = fraction*((x*x) / ((atzeretCounter)*(atzeretCounter + 1)));
        atzeretCounter += 2;

    }
    return result;
}

2 个答案:

答案 0 :(得分:2)

主要问题是使用超出其收敛范围的系列。

正如OP所说“转换x到radX =(x * PI)/ 180”表示OP以度而不是弧度开始,OP很幸运。找到my_sin(x)的第一步是减少范围。以度数开始时,减少是准确的。因此,在转换为弧度之前,请缩小范围

long double calcMaclaurinPolynom(double x /* degrees */, int N){
  // Reduce to range -360 to 360
  // This reduction is exact, no round-off error
  x = fmod(x, 360);  

  // Reduce to range -180 to 180
  if (x >= 180) {
    x -= 180;
    x = -x;
  } else if (x <= -180) {
    x += 180;
    x = -x; 
  }

  // Reduce to range -90 to 90
  if (x >= 90) {
    x = 180 - x;
  } else if (x <= -90) {
    x = -180 - x;
  }

  //now convert to radians.
  x = x*PI/180;
  // continue with regular code

替代方案,如果使用C11,请使用remquo()。搜索SO以获取示例代码。

如上所述@ user3386109,无需“转换回度”。

[编辑]

对于典型的求和系列,将最不重要的术语首先相加可以提高答案的精确度。使用OP的代码,可以使用

完成
for (int i = N; i >= 0; i--)

或者,不是迭代固定次数,而是循环直到该项对总和没有意义。以下使用递归来首先对最不重要的术语求和。范围缩小在-90到90范围内,迭代次数不会过多。

static double sin_d_helper(double term, double xx, unsigned i) {
  if (1.0 + term == 1.0)
    return term;
  return term - sin_d_helper(term * xx / ((i + 1) * (i + 2)), xx, i + 2);
}


#include <math.h>
double sin_d(double x_degrees) {

  // range reduction and d --> r conversion from above
  double x_radians = ...

  return x_radians * sin_d_helper(1.0, x_radians * x_radians, 1);
}

答案 1 :(得分:1)

您可以通过将符号变量合并到fraction中的(-x*x)更新中来避免使用该变量。

使用算法,您可以在阶乘中遇到整数溢出问题。

只要x*x < (2*k)*(2*k+1)错误(假设完全评估)受abs(fraction)限制,即系列中下一个字词的大小。

对于大x,最大的错误来源是截断。浮点误差通过取消交替序列的项而放大。对于kx/2,第k个术语的大小最大,必须被其他大项所抵消。


减半和 - 磨边

在不使用pi值的情况下处理大x的一种简单方法是使用三角定理

sin(2*x)=2*sin(x)*cos(x)
cos(2*x)=2*cos(x)^2-1=cos(x)^2-sin(x)^2

首先通过减半来减少x,同时评估sin(x/2^n)cos(x/2^n)的Maclaurin系列,然后使用三角平方(文字平方作为复数cos(x)+i*sin(x))来恢复原始参数的值。

cos(x/2^(n-1)) = cos(x/2^n)^2-sin(x/2^n)^2
sin(x/2^(n-1)) = 2*sin(x/2^n)*cos(x/2^n)

然后

cos(x/2^(n-2)) = cos(x/2^(n-1))^2-sin(x/2^(n-1))^2
sin(x/2^(n-2)) = 2*sin(x/2^(n-1))*cos(x/2^(n-1))


有关sin和cos值的同时计算,请参见https://stackoverflow.com/a/22791396/3088138,然后用

封装它
def CosSinForLargerX(x,n):
    k=0
    while abs(x)>1:
        k+=1; x/=2
    c,s = getCosSin(x,n)
    r2=0
    for i in range(k):
        s2=s*s; c2=c*c; r2=s2+c2
        s = 2*c*s
        c = c2-s2
    return c/r2,s/r2