我制作了一个计算相应泰勒系列的程序,但是教授希望我们通过先划分然后乘法来改变它。这是我的代码,我在第一部分使用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确实如此,但计算错误。我试图自己解决这个问题,所以只需一个指针就会有所帮助!谢谢!
答案 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的结果(注意:我没有测试这个,这是理论)。编译器完全有可能看到优化机会并将其带给您。