计算三角函数的功能精度降低,误差百分比上升

时间:2015-01-09 06:39:06

标签: c++ trigonometry computation

您好我正在用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编译器上,请给我建议

1 个答案:

答案 0 :(得分:2)

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

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

最好避免使用pow和factorial函数,如果你可以在求和的每一步都便宜地更新组件。例如,我保持pow(x,2 * n + 1)的运行值:它开始设置为x(在n == 0),然后每次n递增时,我将其乘以x * x。因此,在每一步更新此值只需要一次乘法。类似的优化用于阶乘项。

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

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

#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;
}