计算三角函数精度下降,误差百分比上升

Computational Trigonometry functions precision decreasing and error percent rising

你好,我正在用 Taylor Series Expansions

求解 sin(x) 和 cos(x) 等三角函数

问题:我的值并没有错,只是不够精确

我的问题是我是否可以提高这些功能的准确性,我想我已经尝试了一切但我需要你的建议。

double trig::funcsin(int value)
{
    sum = 0;
    //summation
    factorial fac;
    for(int i = 0; i < 7; i++)
    {
        sum += pow((-1), i)*(((double)pow(value, (double)2*i+1)/(double)fac.fact((double)2*i+ 1)));
    }
    return sum;
}
double trig::funccos(int value)
{
    factorial fac;
    sum = 0;
    for(int i = 0;i < 7;i++)
    {
        sum += (pow((-1), i)*((double)pow(value, (double)2*i)/(double)fac.fact((double)2*i)));
    }
    return sum;
}

示例:

实数:-0.7568024953

我的:-0.73207

实数:-0.27941549819

我的:-0.501801

随着 x 变大,输出值以指数速率变得不那么精确。

我是GCC编译器,请多多指教

以下代码演示了 sin() 函数的泰勒级数(关于 x==0)。 如您所知,正弦函数每 2*pi 间隔重复一个相同的循环。 但是泰勒级数只是一个多项式——它需要很多项来近似像正弦这样的摆动函数。并且试图在远离原点的某个点逼近正弦函数将需要如此多的项,以至于累积的误差将给出不令人满意的结果。

为了避免这个问题,我的函数首先将 x 重新映射到以零为中心、介于 -pi 和 +pi 之间的单个循环范围。

最好避免使用 pow 和阶乘函数,如果您可以在求和的每个步骤中廉价地更新组件的话。例如,我为 pow(x, 2*n+1) 保留一个 运行 值:它开始设置为 x(在 n==0),然后每次 n 递增时,我将其乘以 x *X。因此,在每一步更新这个值只需要一次乘法。阶乘项也使用了类似的优化。

这个系列在正项和负项之间交替,所以为了避免跟踪我们是否需要添加或减去一个项的麻烦,循环在每次迭代中处理两个项——它添加第一个并减去第二个第二.

每次计算新的总和时,都会将其与之前的总和进行比较。如果两者相等(说明更新已经超过了sum变量的精度),函数returns。这不是测试终止条件的好方法,但它使函数更简单。

#include <iostream>
#include <iomanip>

double mod_pi(double x) {
    static const double two_pi = 3.14159265358979 * 2;
    const int q = static_cast<int>(x / two_pi + 0.5);
    return x - two_pi * q;
}

double func_sin(double x) {
    x = mod_pi(x);

    double sum = 0;
    double a   = 1;  // 2*n+1   [1, 3, 5, 7, ...]
    double b   = x;  // x^a
    double c   = 1;  // (2*n+1)!

    const double x_sq = x * x;

    for(;;) {
        const double tp = b / c;

        // update for negative term
        c *= (a+1) * (a+2);
        a += 2;
        b *= x_sq;

        const double tn = b / c;
        const double ns = tp - tn + sum;
        if(ns == sum) return ns;
        sum = ns;

        // update for positive term (at top of loop)
        c *= (a+1) * (a+2);
        a += 2;
        b *= x_sq;
    }
}

int main() {
    const double y = func_sin(-0.858407346398077);
    std::cout << std::setprecision(13) << y << std::endl;
}