在 C 中实现正弦和余弦的泰勒级数

Implementing Taylor Series for sine and cosine in C

我一直在按照我的教授给我们的指南进行操作,但我就是找不到哪里出错了。我还一直在研究有关在 C 中实现泰勒级数的其他一些问题。

假设存在 RaiseTo(计算一个数的 x 次方)。

double factorial (int n)
{
    int fact = 1,
    flag;

   for (flag = 1; flag <= n; flag++)
   {
        fact *= flag;
   }

   return flag;
}

double sine (double rad)
{

int flag_2,
    plusOrMinus2 = 0; //1 for plus, 0 for minus 
double sin, 
    val2 = rad,
    radRaisedToX2,
    terms;

terms = NUMBER_OF_TERMS; //10 terms

    for (flag_2 = 1; flag_2 <= 2 * terms; flag_2 += 2)
    {
        radRaisedToX2 = RaiseTo(rad, flag_2);   

        if (plusOrMinus2 == 0)
        {
            val2 -=  radRaisedToX2/factorial(flag_2);
            plusOrMinus2++; //Add the next number
        }

        else
        {
            val2 += radRaisedToX2/factorial(flag_2);
            plusOrMinus2--; //Subtract the next number
        }
    }

    sin = val2;
    return sin;
 }

int main()
{
    int degree;
    scanf("%d", &degree);
    double rad, cosx, sinx;
    rad = degree * PI / 180.00;
    //cosx = cosine (rad);
    sinx = sine (rad);
    printf("%lf \n%lf", rad, sinx);
}

所以在循环中,我得到 rad^x,将它除以从 1 开始的奇数级数的阶乘,然后根据需要加减它,但是当我 运行程序,我得到的输出远高于 1,我们都知道 sin(x) 的极限是 1 和 -1,我真的很想知道我哪里出错了,这样我就可以改进,抱歉,如果这是一个非常糟糕的问题.

在函数 factorial 中,您在分配给函数的 double return 值之前进行 int 乘法运算。阶乘很容易打破 int 范围,例如 20! = 2432902008176640000.

您还return输入了错误的变量 - 循环计数器!

请将局部变量改为double,如

double factorial (int n)
{
    double fact = 1;
    int flag;

    for (flag = 1; flag <= n; flag++)
    {
        fact *= flag;
    }
    return fact;    // it was the wrong variable, and wrong type
}

此外,甚至不需要阶乘计算。请注意,该系列的每一项都将前一项乘以 rad 并除以项号 - 符号发生变化。

任何超过 12! 的值都大于 32 位 int 可以容纳的值,因此这些值会溢出,因此不会 return 您所期望的。

与其每次都计算完整的阶乘,不如看看序列中的每一项相对于前一项的情况。对于任何给定的术语,下一个是前一个的 -((x*x)/(flag_2*(flag_2-1)) 倍。因此,从 x 的项开始,然后为每个连续的项乘以该因子。

还有一个技巧可以在不知道需要多少项的情况下将结果计算到 double 的精度。我将把它留给 reader.

作为练习

另一种相当幼稚的 5 分钟方法涉及计算包含前 20 个左右阶乘的查找 table,即 1! .. 20!这需要很少的内存,并且可以提高 'each-time' 计算方法的速度。进一步的优化可以很容易地在预先计算阶乘的函数中实现,利用每个阶乘与前一个阶乘的关系。

在两个三角函数的循环中有效消除分支(如果 X 执行 Y 否则执行 Z)的方法将再次提供更快的速度。

C代码

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

const int nMaxTerms=20;
double factorials[nMaxTerms];

double factorial(int n)
{
    if (n==1)
        return 1;
    else
        return (double)n * factorial(n - 1.0);
}

void precalcFactorials()
{
    for (int i=1; i<nMaxTerms+1; i++)
    {
        factorials[i-1] = factorial(i);
    }
}

/*
    sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
double taylorSine(double rads)
{
    double result = rads;

    for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
    {
        double curTermValue = pow(rads, (curTerm*2)+1);
        curTermValue /= factorials[ curTerm*2 ];
        if (curTerm & 0x01)
            result -= curTermValue;
        else
            result += curTermValue;
    }
    return result;
}

/*
    cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
double taylorCos(double rads)
{
    double result = 1.0;
    for (int curTerm=1; curTerm<=(nMaxTerms/2)-1; curTerm++)
    {
        double curTermValue = pow(rads, (curTerm*2) );
        curTermValue /= factorials[ (curTerm*2) - 1 ];
        if (curTerm & 0x01)
            result -= curTermValue;
        else
            result += curTermValue;
    }
    return result;
}

int main()
{
    precalcFactorials();
    printf("Math sin(0.5) = %f\n", sin(0.5));
    printf("taylorSin(0.5) = %f\n", taylorSine(0.5));

    printf("Math cos(0.5) = %f\n", cos(0.5));
    printf("taylorCos(0.5) = %f\n", taylorCos(0.5));

    return 0;
}

输出

Math sin(0.5) = 0.479426
taylorSin(0.5) = 0.479426
Math cos(0.5) = 0.877583 
taylorCos(0.5) = 0.877583

Javascript

在 javascript 中实现,当对 sin/cos 函数中的 7 个项求和时,代码产生与内置数学库看似相同的结果(我没有进行太多测试)。

window.addEventListener('load', onDocLoaded, false);

function onDocLoaded(evt)
{
 console.log('starting');
 for (var i=1; i<21; i++)
  factorials[i-1] = factorial(i);
 console.log('calculated');
 
 console.log(" Math.cos(0.5) = " + Math.cos(0.5));
 console.log("taylorCos(0.5) = " + taylorCos(0.5));
 console.log('-');
 console.log("  Math.sin(0.5) = " + Math.sin(0.5));
 console.log("taylorSine(0.5) = " + taylorSine(0.5));
}

var factorials = [];

function factorial(n)
{
 if (n==1)
  return 1;
 else
  return n * factorial(n-1);
}

/*
 sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! .......
*/
function taylorSine(x)
{
 var result = x;
 for (var curTerm=1; curTerm<=7; curTerm++)
 {
  var curTermValue = Math.pow(x, (curTerm*2)+1);
  curTermValue /= factorials[ curTerm*2 ];
  if (curTerm & 0x01)
   result -= curTermValue;
  else
   result += curTermValue;
 }
 return result;
}

/*
 cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! .......
*/
function taylorCos(x)
{
 var result = 1.0;
 for (var curTerm=1; curTerm<=7; curTerm++)
 {
  var curTermValue = Math.pow(x, (curTerm*2));
  curTermValue /= factorials[ (curTerm*2)-1 ];
  if (curTerm & 0x01)
   result -= curTermValue;
  else
   result += curTermValue;
 }
 return result;
}