0

我正在做一个家庭作业,我需要使用 McLaurin 系列来评估 cosh(x) 和 sin(x)。每次程序运行时,用户输入一个从 0 到 10 的数字来指定要评估系列的术语数。然后,用户输入 x 的值以进行评估。一旦用户点击输入,将计算并打印出该系列的 10 个增量。此外,我还必须找到确切的错误。为此,我为 sinh 和 cosh 创建了几个不同的函数,用于执行特定项的计算。例如:下面的代码将术语评估为 10!。

void cosFunction10(double power, double value)
{
     double cosh_x = 0;
     double math_cosh = 0;
     double exact_error;
     double x;

     if (value < 0)
         x = -0.1*0;

     for (int i = 0; i < 11; ++i)
     {
         cosh_x = (1.0 + ((x*x)/(2.0*1.0)) + ((x*x*x*x)/(4.0*3.0*2.0*1.0)) + ((x*x*x*x*x*x)/(6.0*5.0*4.0*3.0*2.0*1.0)) + ((x*x*x*x*x*x*x*x)/(8.0*7.0*6.0*5.0*4.0*3.0*2.0*1.0)) + ((x*x*x*x*x*x*x*x*x*x)/(10.0*9.0*8.0*7.0*6.0*5.0*4.0*3.0*2.0*1.0)));
         math_cosh = cosh(x);
         exact_error = (math_cosh - cosh_x);

         cout << setiosflags(ios::scientific) << setprecision(3) << x << "\t";
         cout << setiosflags(ios::scientific) << setprecision(5) << cosh_x << "\t";
         cout << math_cosh << "\t";
         cout << exact_error << endl;
         x = x + (0.1*value);
     }
 }

如果我运行程序并输入以下值:10(第 n 项)和 -6(x 的值)。

这就是我应该得到的:(第二个增量)

 x                Series            Exact (using the cmath functions)    Exact % Error
 -6.000e-001      1.18547e+000      1.18547e+000                        0.00000e+000

对于确切的错误,接下来的 2 个输出将是相同的,直到我达到第 4 个增量

 x                Series            Exact (using the cmath functions)    Exact % Error
 -1.800e+100      3.10747e+000      3.10747e+000                        -7.67243e-005

当我运行我的代码时,我没有得到上述结果:(第二次增量)

 x                Series            Exact (using the cmath functions)    Exact % Error
-6.000e-001      1.18547e+000      1.18547e+000                         4.55325e-012

所以似乎出了点问题,因为我的确切错误与我得到的错误甚至不一样。我知道可能会有稍微不同的值,这是预期的,但不会达到这个程度。

我们欢迎所有的建议,

谢谢

4

5 回答 5

4

必须有这样的差异,因为cosh(x)根据下面给出的公式以不同的方式定义:

在此处输入图像描述

请参阅此处了解更多详情。

但是,如果您基于 MacLaurin 级数的计算来计算数量,问题是您没有以正确的方式累积值。以下实现可能是您真正需要的:

void cosFunction(double x)
{
    double cosh_x = 1;
    double math_cosh;
    double exact_error;
    double factorial = 1;
    double x_pow = 1;

    for (int i = 0; i < 11; ++i) {
            cosh_x = x_pow/factorial;
            x_pow = x*x*x_pow; 
            factorial = factorial* (2 * i + 1) * (2 * i + 2);
    }

    math_cosh = cosh(x);
    exact_error = (math_cosh - cosh_x);
    cout << setiosflags(ios::scientific) << setprecision(5) << exact_error << endl;

}
于 2012-10-13T03:50:02.350 回答
1

在数学意义上,正确答案可能是零。但在实际计算意义上,它不会为零:

想象一下,如果您正在以固定的十进制精度进行算术运算,例如 8 位数字。您将表示1.0/3.0.33333333。但那会3.0 * (1.0/3.0)变成3.0 * .33333333哪个会是.999999999。因此,虽然在数学意义上,1.0 - (3.0 * (1.0 / 3.0))应该为零,具有固定的小数精度,但您很可能会得到类似.00000001.

相同的规则适用于固定二进制精度,这是您使用double.

于 2012-10-13T05:26:19.033 回答
1

正如评论中所揭示的,您得到的错误实际上非常小,小于 10 -11。此外,您必须考虑到您计算 cosh 的方式和内置函数完成的方式并不完全相同。这两种方法最终都是近似值,但它们的工作方式不同,因此它们产生的舍入误差和近似误差会略有不同。

您不能期望错误为“flat 0”。

于 2012-10-13T04:13:19.647 回答
1
//Part of the problem is that in the line after the 'for' nothing changes (typo?.) The
//following works for x = 0.5`
//Note that the loop works properly for all terms including the first which is 1.0

#include <iostream>
#include <math.h>

using namespace std;

int factorial(int n)
{
    return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;
}

int main()
{
    // use taylor series to approximate cosh(x) - use 7 terms
    double x = 0.5;
    double top, bot;
    double s = 0.0;

    for (int i = 0; i < 7; i++)
    {
        top = pow(x,double(i)*2.0);
        bot = double(factorial(i*2));
        s  += (top / bot);
    }
    cout << "myCosh     " << s << endl;
    cout << "cosh       " << cosh(x)<< endl;
    cout << "difference " << fabs(s - cosh(x)) << endl;
    return 0;
}
于 2012-10-13T06:12:19.350 回答
0

我运行您的函数,cout操作修改如下:

cout << setiosflags(ios::fixed) << setprecision(12);
cout << "for x="<<x<<": "<<math_cosh<<" - "<<cosh_x<<" = "<<exact_error<<"\n";

我为 选择了我自己的值value,因为您没有说明您使用的是什么。

我得到以下结果,这证明了这一点math_cosh并且cosh_x一样。您的问题似乎是您没有看到足够的数字来注意到它们是不同的。

for x=0.003233200000: 1.000005226796 - 1.000005226791 = 0.000000000005
for x=0.006466400000: 1.000020907237 - 1.000020907164 = 0.000000000073
for x=0.009699600000: 1.000047041489 - 1.000047041120 = 0.000000000369
for x=0.012932800000: 1.000083629824 - 1.000083628658 = 0.000000001166
for x=0.016166000000: 1.000130672624 - 1.000130669778 = 0.000000002846
for x=0.019399200000: 1.000188170381 - 1.000188164480 = 0.000000005901
for x=0.022632400000: 1.000256123697 - 1.000256112765 = 0.000000010932
for x=0.025865600000: 1.000334533282 - 1.000334514632 = 0.000000018650
for x=0.029098800000: 1.000423399955 - 1.000423370081 = 0.000000029875
for x=0.032332000000: 1.000522724646 - 1.000522679112 = 0.000000045534
for x=0.035565200000: 1.000632508392 - 1.000632441726 = 0.000000066667

关于家庭作业,使用 Maclaurin 级数计算 cosh(x): 你的函数,如所写,不是很精确,因为它只计算级数的前三个项1 + 0 + x*x/2。您不是在计算 x^n 或 n!超过 n=2。为了产生更精确的结果,您需要积累更多的系列术语 - 如下所示......

int NUMBER_OF_TERMS=10; // the number of terms to compute (including zero terms)
for ( int i=0; i<NUMBER_OF_TERMS; i++ )
   {
   cosh_x += (i%2==0?1:0)*term; // even derivatives are 1, odd derivatives are 0
   term *= x/(i+1); // apply incremental power and factorial
   }

这仍然不能保证准确性。由于舍入误差,您无法确保完全匹配内置函数 cosh(x) ,除非您完全复制其算法。这两个函数都只会产生一个近似值,因为对于大多数输入值,纯数学输出将是一个超越数——而这样的数字不能在 a 中精确表示double

您还应该记住,Maclaurin 级数是基于多项式的近似,在 附近最准确x=0。正如多项式的本质一样,不完整的求和最终可能(随着 x 远离 0)与正确答案非常迅速地偏离。这就是为什么在上面的示例输出中,对于较小的值,误差值较小x


关于科学记数法与定点精度:

科学记数法向您显示数字的最重要的非零数字 - 即使这些数字从小数点右侧的 12 位开始。阅读科学记数法时,重要的是要注意后面的信息,e因为它描述了数字的真实大小。 double-precision 值可以存储大约 20 位十进制数字的真实信息。这对于进行许多重复计算很有用,但在比较两个数字是否相等时通常不是很有意义。

尽管这可能不在您的任务范围内,但您可以做的是限制差异的定点精度。以下是您可以如何执行此操作的示例:

const double PRECISION=1e-4; // indicates the number of fractional digits to keep
exact_error = round(exact_error/PRECISION)*PRECISION; // limit precision

...或者只检查接近零的情况可能更简单...

if ( fabs(exact_error)<PRECISION/2 ) exact_error = 0; // close enough to zero
于 2012-10-13T04:36:01.840 回答