码迷,mamicode.com
首页 > 其他好文 > 详细

c计算sin()函数的近似值,不使用函数库

时间:2015-04-03 13:33:20      阅读:283      评论:0      收藏:0      [点我收藏+]

标签:

首先是自己写的代码如下:

// sinx.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include "math.h"

//该函数计算阶乘
double ProductFunc(double x)
{
	double product =x;

	while (1!=x)
	{
		product *= (--x);
	}
	return product;
}

double myTestFunc(double inputx)
{
	//分子
	double fenzi = inputx;

	//符号
	int fuhao = 1;

	//分母
	double fenmu = 1;

	//分母总和
    double fenmuS = 1;

	//记录总结果总和
	double sum = 0;

	//循环递归调用
	do 
	{
	    //累加计算
		sum += fuhao*fenzi /fenmuS;

		//分子变化
		fenzi *= inputx*inputx;

		//符号变化
		fuhao = fuhao *(-1);

		//分母变化
		fenmu += 2;

		//分母总和变化
		fenmuS = ProductFunc(fenmu);

	} while (abs(fenzi/fenmu)>1e-5);//循环条件

	    //返回总和
		return sum;
}

#define PI 3.1415926

int _tmain(int argc, _TCHAR* argv[])
{

    double inputx = (PI)/6;

	double kk = myTestFunc(inputx);

	double kkk = sin(inputx);

	return 0;
}


对比大牛代码:


先听故事,再编程序。故事是这样的:话说sin和cos是一对夫妇。一天,sin去听相声了,cos在家。过了一会,有人敲门,cos开门一看,是一个不认识的多项式函数。cos问:你是谁啊?他说:我是你的老公sin啊。cos说:你不是去听相声了吗?怎么成这幅摸样了?他说:是啊,太乐了!故事讲完了。不懂吗?好好学高数。否则,挂了不冤。

技术分享
 
编程序求出sin(π/2)、cos(87°)
程序的要求是这样的:(1)求sin、cos时,不能用数学库函数(即不得用#include<Cmath>),而是自己编函数实现,为区别,可以分别起名为mysin和mycos;(2)自定义函数要写在main函数之后;(3)自定义函数的效率问题必须考虑;(4)关于精度:当最后一项的绝对值小于0.00001时,累加结束。


实验目的:学会使用自定义函数解决实际问题
实验内容:定义自定义函数,计算sin和cos的近似值

【先上调试后正确的程序】此程序上我也经历了和大家一样的磨难,犯的错误很“隐蔽”,将在后面细表。

  1. /* 程序头部注释开始 
  2. * 程序的版权和版本声明部分 
  3. * Copyright (c) 2011, 烟台大学计算机学院学生 
  4. * All rights reserved. 
  5. * 文件名称: sin_and_cos.cpp                            
  6. * 作    者: 贺利坚                            
  7. * 完成日期: 2011 年 11 月 22 日 
  8. * 版本 号: v2.0         
  9.   
  10. * 对任务及求解方法的描述部分 
  11. * 输入描述:无 
  12. * 问题描述:自定义函数,用泰勒公式实现计算sin和cos的近似值,要求:(1)求sin、cos时,不能用数学库函数(即不得用#include<Cmath>),而是自己编函数实现,为区别,可以分别起名为mysin和mycos;(2)自定义函数要写在main函数之后;(3)自定义函数的效率问题必须考虑;(4)关于精度:当最后一项的绝对值小于0.00001时,累加结束。 
  13. * 程序输出:sin(π/2)、cos(87°)的值(提示:用泰勒公式在π/2附近误差较大,输出分别为0.911557和-0.26322,而真值分别为1和0.052336,当度数较小时,效果要好一些。) 
  14. * 算法设计:使用泰勒公式 
  15. * 程序头部的注释结束(此处也删除了斜杠) 
  16. */  
  17. #include <iostream>   
  18. #include<Cmath>  //为便于对比结果,main函数中调用了Cmath中的库函数sin和cos  
  19. using namespace std;  
  20. const double pi=3.1415926;  
  21. double mysin(double);  
  22. double mycos(double);  
  23. double myabs(double);   //程序中需要求精度的绝对值,也用自定义函数完成吧  
  24. int main( )  
  25. {  
  26.    cout<<"sin(π/2)的值为"<<mysin(pi/2)<<endl;  
  27.    cout<<"cos(87°)的值为"<<mycos((87.0/180)*pi)<<endl;  
  28.    cout<<"利用库函数求得sin(π/2)的值为"<<sin(pi/2)<<endl;  
  29.    cout<<"利用库函数求得cos(87°)的值为"<<cos((87.0/180)*pi)<<endl;  
  30.    system("PAUSE");  
  31.    return 0;  
  32. }  
  33. //下面定义mysin函数  
  34. double mysin(double x)  
  35. {  
  36.    double sum=x,x_pow=x,item;  
  37.    int n=1,fact=1,sign=1;     //定义变量时赋初值,已经将第一项考虑到累加和sum中  
  38.    do  
  39.    {  
  40.             fact=fact*(n+1)*(n+2);  //fact用于表示阶乘,在公式中作分母  
  41.              x_pow*=x*x;             //x_pow是分子中用于表示阶乘,在公式中作分母  
  42.              sign=-sign;             //确定即将要累加的这一项的符号  
  43.              item =x_pow/fact*sign; //计算出要累加的项  
  44.              sum+=item;              //将该项累加上去  
  45.              n+=2;  
  46.   }while(myabs(item)>1e-5);  
  47.    return sum;  
  48. }  
  49.    
  50. //下面定义mycos函数  
  51. double mycos(double x)  
  52. {  
  53.    double sum=1,x_pow=1,item;  
  54.    int n=0,fact=1,sign=-1;  
  55.    do  
  56.    {  
  57.             fact=fact*(n+1)*(n+2);  
  58.              x_pow*=x*x;  
  59.              item =x_pow/fact*sign;  
  60.              sum+=item;  
  61.              sign=-sign;  
  62.              n+=2;  
  63.   }while(myabs(item)>0.00001);  
  64.    return sum;  
  65. }  
  66.    
  67. //下面定义myabs函数  
  68. double myabs(double x)  
  69. {  
  70.    return ((x>=0)?x:-x);  
  71. }  

运行结果:

 技术分享

 

经验积累:

1. 做科学计算时,需要对所用方法的数学性质有所了解

2. 取合适的变量名(sum,x_pow,item,n,fact,sign)有助于以一种清晰的思路解题,保证了程序的可读性

3. 对于复杂的计算,不妨多设几个变量,将他们间的关系分清楚,可以会多费些内存,但对正确性的保证无可替代

 

  【下面讲讲我犯的愚蠢的错误】仔细看看这个程序,和我一起分析清楚问题,是我对大家最大的贡献。

  原先,我的nysin函数是这样写的,貌似合理:

  1. double mysin(double x)  
  2. {  
  3.  double sum=x,item=x;  
  4.  int n=1,fact=1,sign=-1;  
  5.  do  
  6.  {  
  7.   fact=fact*(n+1)*(n+2);  //求阶乘  
  8.   item =item*x*x/fact*sign;  //要加的项  
  9.   sum+=item;   //累加  
  10.   sign=-sign;  
  11.   n+=2;  
  12.  }while(myabs(item)>1e-5);  
  13.  return sum;  
  14. }  
  据此计算得到sin(pi/2)的结果是0.911557。人的心理一般是这样的,没错呀?怎么会错呢?我怎么会错呢?看了一遍又一遍,结果当然是我没有错,然而输出结果和库函数给出的结果就是不一样。上周三我在准备和这个错误一直作斗争,已经过了12:00点了,得吃饭,13:20 需要从家出发,去教室上课,但是实验报告的模板还得出来。

  怎么办?泰勒公式会有问题?我没错!带着这个心理,我在指导书写下了泰勒公式可能的误差的文字。现在想来,这是多么不严谨的做法,以至于后来一再改模板。

  逐渐想到,还是程序中有错误。我们现在一起找一下。

  针对泰勒公式:

  在变量定义的同时,通过赋初值,已经考虑了将x加到sum中:

     double sum=x,item=x;

     int n=1,fact=1,sign=-1;

  下面需要构造进入循环以后将各项一正一负地累加到sum中。

  当第1次进行循环:

    fact=fact*(n+1)*(n+2);  得到了3!

    item =item*x*x/fact*sign;  求得要加的项是-x^3/3!,没错。(x^3表示x的3次方,在此只为方便表达,并不是C++中的合法运算。)

    sum+=item; 得到了x-x^3/3!。

    随后sign变为1、n自增2变为5。都没有问题。

  第2次进入循环:

    fact=fact*(n+1)*(n+2);  得到了5!,没错

    item = item*x*x/fact*sign;呢?这时才发现item将变为(-x^3/3!)*x*x/5!*1=-(x^5)/(3!*5!),不仅分母不对,符号也不对。

  !!!!

  这时,只有敲自己的脑袋了(师生似乎都一样的)。

  于是,有了上面提交的结果。

  仔细反思,这儿犯的错误根源是,让item变量承担了多项职责。我们设置变量的原则是,每个变量的功能尽可能单一。

  再看正确做法中,fact、 x_pow、item 、sum和sign的含义,清楚多了。很多同学的程序中,即使功能单一,用诸如a、b、c、d、e、f、g等做变量,只能搞糊涂自己。

  至于mycos,类似的问题,不再多说。

 

  【同学们解法中的一个典型错误】

  有不止一位同学的程序中,函数定义由double mysin(double x)开始,这没有问题,x是形式参数。

  但是在函数体内,有些同学对x重新赋值:x=pi/2;有些同学将本该出现x的地方直接写作了(pi/2)。的确,这样做能够求出sin(pi/2)的值,但是,这也使得你定义的函数只能求得sin(pi/2)的值了。我后悔在任务中应该多加一个求sin(pi/4),这样能启发同学们改过来。

  一定要注意,尽管我们编程序解决某个具体问题,但是写出的函数还是要“通用”一些才好,这体现出的是程序与数据的“独立性”。慢慢体会吧。


看大牛的结果挺好,于是决定,将自己的代码与大牛对比:

关于精度控制,不好控制,输出代码中可以看出,用泰勒公式5次以后就可以完全近似sin的值(对90度来说);

对比代码如下:

// sinx.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include "math.h"

#include "iostream"

using namespace std;
//该函数计算阶乘
double ProductFunc(double x)
{
	double product =x;

	while (1!=x)
	{
		product *= (--x);
	}
	return product;
}

double myTestFunc(double inputx)
{
	//分子
	double fenzi = inputx;

	//符号
	int fuhao = 1;

	//分母
	double fenmu = 1;

	//分母总和
    double fenmuS = 1;

	//记录总结果总和
	double sum = 0;
    int i = 1;
	//循环递归调用
	do 
	{
	    cout<< "第"<<i<<"次循环,分子为:"<<fenzi<<endl;
		cout<< "第"<<i<<"次循环,分母为:"<<fenmuS<<endl;

	    //累加计
		sum += fuhao*fenzi /fenmuS;

        cout<< "第"<<i++<<"次循环"<<"总和为:"<<sum<<endl;

		//分子变化
        double kkkk = fenzi;
		fenzi *= inputx*inputx;

		//符号变化
		fuhao = fuhao *(-1);

		//分母变化
		fenmu += 2;

		//分母总和变化
		fenmuS = ProductFunc(fenmu);


	} while (i<7/*(fenzi/fenmu)>1e-1*/);//循环条件

	    //返回总和
		return sum;
}


//下面定义myabs函数
double myabs(double x)
{
	return ((x>=0)?x:-x);
}

//下面定义mysin函数
double mysin(double x)
{
	double sum=x,x_pow=x,item;
	int n=1,fact=1,sign=1;     //定义变量时赋初值,已经将第一项考虑到累加和sum中
	int i=1;
	do
	{

		cout<< "对比第"<<i++<<"次循环"<<"总和为:"<<sum<<endl;

		fact=fact*(n+1)*(n+2);  //fact用于表示阶乘,在公式中作分母
		x_pow*=x*x;             //x_pow是分子中用于表示阶乘,在公式中作分母
		sign=-sign;             //确定即将要累加的这一项的符号
		item =x_pow/fact*sign; //计算出要累加的项
		sum+=item;              //将该项累加上去
		n+=2;

	}while(i<7/*myabs(item)>1e-1*/);
		return sum;
}

#define PI 3.1415926

int _tmain(int argc, _TCHAR* argv[])
{

    double inputx = PI/2;

	double k = mysin(inputx);

	double kk = myTestFunc(inputx);

	double kkk = sin(inputx);

	system("pause");
	return 0;
}

c计算sin()函数的近似值,不使用函数库

标签:

原文地址:http://blog.csdn.net/ghevinn/article/details/44852355

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!