C中的泰勒级数,其函数先划分再乘以

时间:2016-10-04 18:23:53

标签: c function

我制作了一个计算相应泰勒系列的程序,但是教授希望我们通过先划分然后乘法来改变它。这是我的代码,我在第一部分使用divide()。我的power()和fact()只是对旧代码的引用。

#include <stdio.h>
#include <math.h>

int main()
{
double  x, mySin(), myCos(), myExp();
char    more;

do      {
    printf ("\n\t\t\t\t\tInput x: ");
    scanf  ("%lf", &x);
    printf ("\n\t\t\t\t\t\t\tLibraryResult  \t MyResult");
    printf ("\n\t\t\tSin(%6.2f)\t      %9.6f\t\t%9.6f", x, sin(x), mySin(x));
    printf ("\n\t\t\tCos(%6.2f)\t      %9.6f\t\t%9.6f", x, cos(x), myCos(x));
    printf ("\n\t\t\tExp(%6.2f)\t      %9.6f\t\t%9.6f", x, exp(x), myExp(x));
    printf ("\n\n\t\t\t\t\tDo more (Y/N) ?");
    scanf  ("%s", &more);
} while (more == 'y' || more == 'Y');
}

double mySin(double x)
{
double sum = 0., divide();
int    i, sign = 1;

for (i = 0; i < 30; i++, sign = - sign)
    sum = sum + sign * divide(x) ; //power(x, 2 * i + 1) / fact(2 * i + 1);

return sum;
}

double myCos(double x)
{
double sum = 0., divide();
int    i, sign = 1;

for (i = 0; i < 30; i++, sign = - sign)
    sum = sum + sign * divide(x);//power(x, 2 * i) / fact(2 * i);

return sum;

}

double myExp(double x)
{
double sum = 0., divide();
int i;

for (i= 0; i < 30; i++)
    sum = sum + divide(x); //power(x, i) / fact(i);


return sum;
}


double divide(int n, double x)
{
int i;
double div = 1.;

for (i = 1; i < n; i++)
    div =  x / i;

return div;

}

/*double fact(int n)
{
int i;
double prod = 1.;

for (i = 1; i <= n; i++)
    prod = prod * i;

return prod;
}

double power (double x, int n)
{
int i;
double prod = 1.;

for (i = 0; i < n; i++)
    prod = prod * x;

return prod;
}
*/

我一直在搞乱代码,当在divide()中时,使用i&lt;当我输入x时,n不会产生任何结果。但是,使用i&lt; 30确实如此,但计算错误。我试图自己解决这个问题,所以只需一个指针就会有所帮助!谢谢!

1 个答案:

答案 0 :(得分:1)

我想我会给你答案,因为你的问题是系统性的。

首先,将函数拉出到前向声明中。不再:

double  x, mySin(), myCos(), myExp();

而是将其置于int main()

之上
double divide(int n, double x);
double mySin(double x);
double myCos(double x);
double myExp(double x);

这也为您提供了记录这些功能的绝佳机会。这实际上解决了你的问题! (是的,即使文档只是一个评论):

// Returns x^n / n!
double divide(int n, double x);

看着那条评论,突然mySin毫无意义:

sum = sum + sign * divide(x); // this is sum += sign * x^? / ?!

因此,纠正mySin会看起来像这样:(注意,从循环语句中提取逻辑通常是一个好主意)

// The series for sin(x) starts with x^1/1 and continues by increasing by 2
for (i = 1; i < 30; i += 2) {
    sum += sign * divide(i,x); // += saves you from writing sum + ...
    sign = -sign;
}

这应该产生正确的输出,但事实并非如此。很遗憾,您divide()也遇到了问题。记住它的文档,让我们来看看它:

double divide(int n, double x)
{
int i;
double div = 1.;

for (i = 1; i < n; i++)
    div =  x / i;

return div;

}

这非常接近正确,只是等号不太正确。我们希望每次迭代乘以x / i。

for (i = 1; i < n; i++)
    div *= x / i;

距离更近,但是让我们计算循环的次数。它的n-1次(如果我们从0开始它将是n次,但是我们有0除以误差)。我们来解决这个问题:

for (i = 1; i <= n; i++)
    div *= x / i;

现在这在技术上与乘法同时进行除法,这将产生最准确的结果(通过将div保持在“正常”数字附近)。如果你先划分,你将开始使用非常小的数字,如果你先加倍,你将开始使用非常大的数字。无论哪种方式,你都会失去准确性(因为对于中等大小的数字,双精度最准确)。如果将该循环分开,则变为:

划分循环:

for (i = 1; i <= n; i++)
    div /= i;

乘法循环:

for (i = 1; i <= n; i++)
    div *= x;

对于大多数输入,它根本没有太大的区别。但是,如果你有“极端”值,你最终会杀死你的双打。例如,当首先乘以时,如果使用x = 1000,则在实际开始分割之前,您将超出双精度空间。结果将是nan,因为double只是无法处理它。类似地,如果你抽取迭代(而不是30,做30000000)它显然需要更长的时间(当然大约100000倍),但如果你也先划分,你最终会在你成倍增加之前将你的双倍归零。因此,你将得到0的结果(注意:我没有测试这个,这是理论)。编译器完全有可能看到优化机会并将其带给您。