c++ - C++-使用泰勒级数逼近估算cos(x)

标签 c++ double precision taylor-series

为了获得更多使用C++的实践,我决定不使用数学库就做一些基本的数学函数。我已经完成了功能和阶乘功能,它们似乎运行良好。但是,我的泰勒级数余弦函数存在很多问题。

Wikipedia Cosine Taylor Series

它在cos(1),cos(2)处输出良好的近似值,并在cos(3)和cos(4)处开始失去精度。除此之外,它的答案变得完全错误。以下是来自./a.out的结果

Input an angle in radians, output will be its cosine
1
Output is: 0.540302

Input an angle in radians, output will be its cosine
2
Output is: -0.415873

Input an angle in radians, output will be its cosine
3
Output is: -0.974777

Input an angle in radians, output will be its cosine
4
Output is: -0.396825                       <-------------Should be approx. -0.654

Input an angle in radians, output will be its cosine
5
Output is: 2.5284                          <-------------Should be approx.  0.284

这是完整的源代码:
#include <iostream>
#include <iomanip>

using std::cout;
using std::cin;
using std::endl;

int factorial(int factorial_input) {

    int original_input = factorial_input;
    int loop_length = factorial_input - 1;

    if(factorial_input == 1 || factorial_input == 0) {

        return 1;
    }

    for(int i=1; i != loop_length; i++) {

        factorial_input = factorial_input - 1;

        original_input = original_input * factorial_input;

    }

    return original_input;
}

double power(double base_input, double exponent_input) {

    double power_output = base_input;

    if(exponent_input == 0) {

        return 1;
    }

    if(base_input == 0) {

        return 0;
    }

    for(int i=0; i < exponent_input -1; i++){

        power_output = power_output * base_input;

    }
    return power_output;

}

double cos(double user_input) {

    double sequence[5] = { 0 };  //The container for each generated elemement.
    double cos_value = 0;        //The final output.
    double variable_x = 0;       //The user input x, being raised to the power 2n

    int alternating_one = 0;     //The (-1) that is being raised to the nth power,so switches back and forth from -1 to 1 
    int factorial_denom = 0;     //Factorial denominator (2n)!
    int loop_lim = sizeof(sequence)/sizeof(double);            //The upper limit of the series (where to stop), depends on size of sequence. Bigger is more precision.

    for(int n=0; n < loop_lim; n++) {

        alternating_one = power(-1, n);
        variable_x = power(user_input, (n*2));
        factorial_denom = factorial((n*2));

        sequence[n] =  alternating_one * variable_x / factorial_denom;
        cout << "Element[" << n << "] is: " << sequence[n] << endl;     //Prints out the value of each element for debugging.
    }

    //This loop sums together all the elements of the sequence.
    for(int i=0; i < loop_lim; i++) {                   

        cos_value = cos_value + sequence[i];

    }

    return cos_value;
}

int main() {

    double user_input = 0;
    double cos_output;

    cout << "Input an angle in radians, output will be its cosine" << endl;
    cin >> user_input;
    cos_output = cos(user_input);

    cout << "Output is: " << cos_output << endl;

}

根据Desmos上的这张图,在五次迭代中,我的函数应保持精度,直到x> 4.2左右为止:

Desmos Graph

同样,当我将系列设置为使用20次或更多次迭代时(它会生成越来越小的数字,这将使答案更加精确),元素开始表现得非常不可预测。这是启用了序列调试器的./a.out,以便我们可以看到每个元素包含的内容。输入为1。
Input an angle in radians, output will be its cosine
1
Element[0] is: 1
Element[1] is: -0.5
Element[2] is: 0.0416667
Element[3] is: -0.00138889
Element[4] is: 2.48016e-05
Element[5] is: -2.75573e-07
Element[6] is: 2.08768e-09
Element[7] is: -7.81894e-10
Element[8] is: 4.98955e-10
Element[9] is: 1.11305e-09
Element[10] is: -4.75707e-10
Element[11] is: 1.91309e-09
Element[12] is: -1.28875e-09
Element[13] is: 5.39409e-10
Element[14] is: -7.26886e-10
Element[15] is: -7.09579e-10
Element[16] is: -4.65661e-10
Element[17] is: -inf
Element[18] is: inf
Element[19] is: -inf
Output is: -nan

谁能指出我做错了什么,我应该做得更好?我是C++的新手,所以我仍然有很多误解。非常感谢您抽出宝贵的时间阅读本文!

最佳答案

您有以下问题:

在图中,您显示的是图片k包含在总和中,而您将其排除在代码中。因此,Desmos图中的k=5等于您代码中的double sequence[6] = { 0 }

这将修复user_input = 4的输出。

然后,对于user_input = 5,您可以将其与图表进行比较,以查看它也给出了相似的结果(与真实值相差很远)

然后,您将遇到大量术语的错误,因为阶乘函数输出int,但是阶乘增长得如此之快,以至于int可以快速容纳的值超出了范围,并且很快超出了任何范围整数类型。如果要支持(尽管不是很多)更大的输入范围,则应返回double并将original_input也设置为double

power中,您将指数作为double,但是将其当作整数使用。特别是将它用于循环迭代的限制。只要这些值足够小以可由double准确表示,就可以正常工作。一旦值变大,循环迭代次数将变得不精确。

请使用int作为power的第二个参数。

如果要用这种方法实现cos,通常首先要使用cos对称性,以将范围缩小到较小的范围,例如首先使用例如[0,pi/2]cos(x + 2pi) = cos(x)cos(x+pi) = - cos(x)以及cos(-x) = cos(x)等。

关于c++ - C++-使用泰勒级数逼近估算cos(x),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59230783/

相关文章:

c++ - 未定义引用 [USRP] [UHD]

c++ - 如何在 OpenGL 中有效地为 2D Sprite 制作动画?

Java十进制格式错误输出

performance - 为什么比较 double 比 uint64 快?

c++ - 在 C++ 中使用 pow 时精度损失

java - 非整数 ULP 错误?

python - 从 C++ dll 到 Python 的指针 vector 上返回指针

c++ - 遍历未知数量的维度

java - 为什么以不同的顺序将三个 double 值相乘会得到不同的答案?

c++ - OpenCL 浮点精度