简体   繁体   English

C 中的泰勒级数(sin(240) 和 sin(300) 的问题)

[英]Taylor Series in C (problem with sin(240) and sin(300))

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

const int TERMS = 7;
const float PI =  3.14159265358979;

int fact(int n) {
    return n<= 0 ? 1 : n * fact(n-1);
}


double sine(int x) {
    double rad = x * (PI / 180);
    double sin = 0;


    int n;
    for(n = 0; n < TERMS; n++) { // That's Taylor series!!
        sin += pow(-1, n) * pow(rad, (2 * n) + 1)/ fact((2 * n) + 1);
    }
    return sin;
}


double cosine(int x) {
    double rad = x * (PI / 180);
    double cos = 0;

    int n;

    for(n = 0; n < TERMS; n++) { // That's also Taylor series!
         cos += pow(-1, n) * pow(rad, 2 * n) / fact(2 * n);
    }
    return cos;
  }

int main(void){
   int y;
   scanf("%d",&y);
   printf("sine(%d)= %lf\n",y, sine(y));
   printf("cosine(%d)= %lf\n",y, cosine(y));

  return 0;
}

The code above was implemented to compute sine and cosine using Taylor series.上面的代码是使用泰勒级数计算正弦和余弦的。 I tried testing the code and it works fine for sine(120).我尝试测试代码,它适用于正弦(120)。 I am getting wrong answers for sine(240) and sine(300).我得到了正弦(240)和正弦(300)的错误答案。

Can anyone help me find out why those errors occur?谁能帮我找出为什么会发生这些错误?

You should calculate the functions in the first quadrant only [0, pi/2).您应该只计算第一象限中的函数 [0, pi/2)。 Exploit the properties of the functions to get the values for other angles.利用函数的属性来获取其他角度的值。 For instance, for values of x between [pi/2, pi), sin(x) can be calculated by sin(pi - x).例如,对于 [pi/2, pi) 之间的 x 值,sin(x) 可以通过 sin(pi - x) 计算。

The sine of 120 degrees, which is 40 past 90 degrees, is the same as 50 degrees: 40 degrees before 90. Sine starts at 0, then rises toward 1 at 90 degrees, and then falls again in a mirror image to zero at 180. 120 度的正弦,即 90 度后的 40 度,与 50 度相同:90 之前的 40 度。正弦从 0 开始,然后在 90 度处向 1 上升,然后在镜像中再次下降到 180 度处的零.

The negative sine values from pi to 2pi are just -sin(x - pi).从 pi 到 2pi 的负正弦值只是 -sin(x - pi)。 I'd handle everything by this recursive definition:我会通过这个递归定义来处理所有事情:

sin(x):
  cases x of:
    [0, pi/2)   -> calculate (Taylor or whatever)
    [pi/2, pi)  -> sin(pi - x)
    [pi/2, 2pi) -> -sin(x - pi)
    < 0         -> sin(-x)
    >= 2pi      -> sin(fmod(x, 2pi))  // floating-point remainder

A similar approach for cos , using identity cases appropriate for it. cos的类似方法,使用适合它的身份案例。

The key point is:关键点是:

TERMS is too small to have proper precision. TERMS太小而无法获得适当的精度。 And if you increase TERMS , you have to change fact implementation as it will likely overflow when working with int .如果增加TERMS ,则必须更改fact实现,因为在使用int时它可能会溢出。

I would use a sign to toggle the -1 power instead of pow(-1,n) overkill.我会使用一个符号来切换 -1 功率而不是pow(-1,n)过度杀伤。

Then use double for the value of PI to avoid losing too many decimals然后对 PI 的值使用double以避免丢失太多小数

Then for high values, you should increase the number of terms (this is the main issue).然后对于高值,您应该增加术语的数量(这是主要问题)。 using long long for your factorial method or you get overflow.对阶乘方法使用long long ,否则会溢出。 I set 10 and get proper results:我设置了 10 并得到了正确的结果:

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

const int TERMS = 10;
const double PI =  3.14159265358979;

long long fact(int n) {
    return n<= 0 ? 1 : n * fact(n-1);
}
double powd(double x,int n) {
    return n<= 0 ? 1 : x * powd(x,n-1);
}


double sine(int x) {
    double rad = x * (PI / 180);
    double sin = 0;


    int n;
    int sign = 1;
    for(n = 0; n < TERMS; n++) { // That's Taylor series!!
        sin += sign  * powd(rad, (2 * n) + 1)/ fact((2 * n) + 1);
        sign = -sign;
    }
    return sin;
}


double cosine(int x) {
    double rad = x * (PI / 180);
    double cos = 0;

    int n;
    int sign = 1;
    for(n = 0; n < TERMS; n++) { // That's also Taylor series!
         cos += sign * powd(rad, 2 * n) / fact(2 * n);
         sign = -sign;
    }
    return cos;
  }

int main(void){
   int y;
   scanf("%d",&y);
   printf("sine(%d)= %lf\n",y, sine(y));
   printf("cosine(%d)= %lf\n",y, cosine(y));

  return 0;
}

result:结果:

240
sine(240)= -0.866026
cosine(240)= -0.500001

Notes:笔记:

  • my recusive implementation of pow using successive multiplications is probably not needed, since we're dealing with floating point.可能不需要我使用连续乘法对pow的递归实现,因为我们正在处理浮点数。 It introduces accumulation error if n is big.如果 n 很大,它会引入累积误差。
  • fact could be using floating point to allow bigger numbers and better precision. fact可以使用浮点来允许更大的数字和更好的精度。 Actually I suggested long long but it would be better not to assume that the size will be enough.实际上我建议long long但最好不要假设大小就足够了。 Better use standard type like int64_t for that.最好使用像int64_t这样的标准类型。
  • fact and pow results could be pre-computed/hardcoded as well. factpow结果也可以预先计算/硬编码。 This would save computation time.这将节省计算时间。
const double TERMS = 14;
const double PI = 3.14159265358979;

double fact(double n) {return n <= 0.0 ? 1 : n * fact(n - 1);}

double sine(double x)
{
    double rad = x * (PI / 180);
    rad = fmod(rad, 2 * PI);
    double sin = 0;

    for (double n = 0; n < TERMS; n++) 
        sin += pow(-1, n) * pow(rad, (2 * n) + 1) / fact((2 * n) + 1);

    return sin;
}

double cosine(double x)
{
    double rad = x * (PI / 180);
    rad = fmod(rad,2*PI);
    double cos = 0;

    for (double n = 0; n < TERMS; n++)
        cos += pow(-1, n) * pow(rad, 2 * n) / fact(2 * n);

    return cos;
}

 int main()
{

    printf("sine(240)= %lf\n",  sine(240));
    printf("cosine(300)= %lf\n",cosine(300));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM