【问题标题】:Computational Trigonometry functions precision decreasing and error percent rising计算三角函数精度下降和误差百分比上升
【发布时间】:2015-03-07 10:52:56
【问题描述】:

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

【问题讨论】:

    标签: c++ trigonometry computation


    【解决方案1】:

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

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

    如果您可以在求和的每个步骤中廉价地更新组件,则最好避免使用 pow 和阶乘函数。例如,我为 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;
    }
    

    【讨论】:

    • 哇这个函数真的很准确!干得好!
    • mod_pi 函数的用途是什么?
    • @Bot mod_pi 从他的回答中完成了这个陈述:“为了避免这个问题,我的函数首先将 x 重新映射到以零为中心的单个循环范围,介于 -pi 和 +pi 之间。” mod 部分可能表示 Modular Arithmetic,此处轻微用词不当,因为模运算用于整数,而不是实数。
    猜你喜欢
    • 2021-03-24
    • 1970-01-01
    • 2015-07-11
    • 2020-11-27
    • 2020-12-25
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    相关资源
    最近更新 更多