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

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

// 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的近似值

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

[cpp] view
plain
copyprint?

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

运行结果:

经验积累:

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

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

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

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

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

[cpp] view
plain
copyprint?

  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;
}
时间: 2024-10-29 19:10:14

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

计算两个时间差的两个函数

计算两个时间差的两个函数  两个时间之差- (NSString *)intervalFromLastDate: (NSString *) dateString1 toTheDate:(NSString *) dateString2{NSArray *timeArray1=[dateString1 componentsSeparatedByString:@"."];dateString1=[timeArray1 objectAtIndex:0]; NSArray *timeArray2=

转 Lua标准库: table函数, 数学函数, 字符串函数/格式化/配对, WoW新增函数, 函数别名

这里只介绍和插件编写比较有关的几个函数. 详细的Lua手册请参照Lua Reference Manual 5.1. assert(value) - 检查一个值是否为非nil, 若不是则(如果在wow.exe打开调试命令)显示对话框以及输出错误调试信息 collectgarbage() - 垃圾收集器. (新增于1.10.1) date(format, time) - 返回当前用户机器上的时间. error("error message",level) - 发生错误时,输出一条定义的错误

关于gcc内置函数和c隐式函数声明的认识以及一些推测

最近在看APUE,不愧是经典,看一点就收获一点.但是感觉有些东西还是没说清楚,需要自己动手验证一下,结果发现需要用gcc,就了解一下. 有时候,你在代码里面引用了一个函数但是没有包含相关的头文件,这个时候gcc报的错误比较诡异,一般是这样:[math.c:6:25: 警告:隐式声明与内建函数‘sin’不兼容 [默认启用]].这个错误网上大量博客都在说需要包含XXX.h文件,但是没有人解释这个错误信息为什么这样表达.什么是隐式声明,什么是内建函数,我就纠结了. 隐式声明函数的概念网上有相关的资料,

匿名函数,子函数,私有函数,重载函数,eval和feval函数

匿名函数,子函数,私有函数等函数类型 匿名函数: 匿名函数没有函数名,也不是.m文件,只包含一个表达式和输入输出参数. [email protected](x,y)x.^y+3*x*y x,y为输入输入参数,Fxy为函数名 子函数: 在 Matlab中, 多个函数写入一个.m文件中.其中出现的第一个函数称为主函数,其他函数称为子函数,保存时文件名与主函数名相同,外部程序只能调用主函数. 特点: 子函数只能被同一文件下的其他函数调用 通过名称调用函数时优先调用子函数,再调用内置函数. 同一文件的主

C++学习笔记(2)---2.5 C++函数编译原理和成员函数的实现

转载自:http://c.biancheng.NET/cpp/biancheng/view/2996.html点击打开链接 从上节的例子可以看出,对象的内存模型中只保留了成员变量,除此之外没有任何其他信息,程序运行时不知道 obj 的类型为 Demo,也不知道它还有一个成员函数 display().那么,究竟是如何通过对象调用成员函数的呢? C++函数的编译 C++和C语言的编译方式不同.C语言中的函数在编译时名字不变,或者只是简单的加一个下划线_(不同的编译器有不同的实现),例如,func()

ORACLE函数之日期时间运算函数

1            ADD_MONTHS 格式:ADD_MONTHS(D,N) 说明:返回日期时间D加N月后对应的日期时间.N为正时则表示D之后:N为负时则表示为D之前:N为小数则会自动先删除小数部分,而用整数部分 举例: SQL>SELECT ADD_MONTHS(SYSDATE,7) A,ADD_MONTHS(SYSDATE,-7) B,ADD_MONTHS(SYSDATE,7.9)C FROM DUAL; A                             B        

JS的全局函数eval解析JSON字符串函数

JavaScript eval() 函数 定义和用法 eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码. 语法 eval(string) 参数 描述 string 必需.要计算的字符串,其中含有要计算的 JavaScript 表达式或要执行的语句. 返回值 通过计算 string 得到的值(如果有的话). 说明 该方法只接受原始字符串作为参数,如果 string 参数不是原始字符串,那么该方法将不作任何改变地返回.因此请不要为 eval() 函数传递 String

java mysql自定义函数UDF之调用c函数

正如sqlite可以定义自定义函数,它是通过API定义c函数的,不像其他,如这里的mysql.sqlite提供原生接口就可以方便的调用其他语言的方法,同样的mysql也支持调用其它语言的方法. google "mysql call c function"发现一片文章 MySQL User Defined Functions  This tutorial explains what an User Defined Function (UDF) is, what it does and w

POJ2480 Longge&#39;s problem 欧拉函数的应用 &amp;&amp; 积性函数

题意很简单,求sum(gcd(i,n))   1<=i<=n; 这题看到后第一反应并没有里用积性函数的性质,不过也可以做,欣慰的是我反应还是比较快的 设f(n)=gcd(1,n)+gcd(2,n)+....+gcd(n-1,n) + gcd(n,n), 用g(n,i)表示满足 gcd(x,n)=i的 x的个数 (x小于n),则 f(n)=sum{i*g(n,i)}; 同时又利用 扩展欧几里德的性质  gcd(x,n)=i  的充要条件是 gcd(x/i,n/i)==1,所以 满足 x/i的解有