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