天天看点

大作业:计算器

翁恺老师的计算器大作业~

改掉了逆波兰表达式的一些bug如不能算小数等等

现在是真正的计算器了!

//功能强大的计算器
//可计算加减乘除乘方阶乘,三角函数,反三角函数,自然对数,常用对数,高斯函数(floor),以e为底的指数函数(exp)
//如果函数后面是非负常数则不必加括号 如sin5,arcsin0.5等;如果是表达式或负数则需加括号 如cos(-0.5),ln(5!) 
//BY Peter_H
//2019.3.30 15:00--20:00
//2019.3.31 12:45--1:00 debug 干掉一个小bug
//2019.3.31 19:00--19:30 增加指导界面 
//2019.4.2 21:50--22:20 修复一个bug  
//修复前计算表达式sqrt(((200.2-200.8)^2+(200.55-200.8)^2+(200.9-200.8)^2+(201.15-200.8)^2+(201.2-200.8)^2)/20)时会出错
//1.添加了对括号的强判断 2.入栈前打入一个括号 

// 2021.6.13 10:50--11:30
// 增加了mode选择功能
// 删去了一些不用的代码,a1改回了a

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include <stdbool.h>
char stack[10000];//存符号 
char cqueue[10000];//存字符串sin cos等 
char out[10000],in[10000];//输出 输入 
int dtop,cfront=1,top,len,ctail;
double dstack[10000];//存数字 
void beforecal(char *a);


char scan_char(void)
{
	// scan without space or \n
	char ch;
	scanf("%c",&ch);
	while(ch==' '||ch=='\n')
	{
		scanf("%c",&ch);
	}
	return ch;
}
void get(char *a)
{
	// printf("%s",a);

	int n=0;
	char ch;
	scanf("%c",&ch); 
	while(ch==' '||ch=='\n')
	{
		scanf("%c",&ch); 
	}
	while(ch!='\n')
	{
		a[n++]=ch;
		scanf("%c",&ch);
	}
	return ;
}
int f1(int x)//阶乘 
{
	if(x==0||x==1)return 1; 
	else return x*f1(x-1);
}
double f2(char *d,double x)//三角函数等 
{
	
    if(strcmp(d,"sin")==0)return sin(x);
    else if(strcmp(d,"cos")==0)return cos(x);
    else if(strcmp(d,"tan")==0)return tan(x);
    else if(strcmp(d,"cot")==0)
	{
		if(x==0)
		{
			printf("输入有误!\n");
			return 0;
		}
		return 1/tan(x);
	}
    else if(strcmp(d,"csc")==0)
	{
		if(x==0)
		{
			printf("输入有误!\n");
			return 0;
		}
		return 1/sin(x);
	} 
    else if(strcmp(d,"sec")==0)return 1/cos(x);
    else if(strcmp(d,"arctan")==0)return atan(x);
    else if(strcmp(d,"arccos")==0)
	{
		if(x>1||x<-1)
		{
			printf("输入有误!\n");
			return 0;
		}
		return x;	
	}
    else if(strcmp(d,"arcsin")==0)
	{
		if(x>1||x<-1)
		{
			printf("输入有误!\n");
			return 0;
		}
		return asin(x);
	}
    else if(strcmp(d,"exp")==0)return exp(x);
	else if(strcmp(d,"sinh")==0)return sinh(x);
	else if(strcmp(d,"cosh")==0)return cosh(x);
	else if(strcmp(d,"tanh")==0)return tanh(x);
	else if(strcmp(d,"ln")==0)
	{
		if(x<=0)
		{
			printf("输入有误!\n");
			return 0;
		}
		return log(x);
	}
	else if(strcmp(d,"lg")==0)
	{
		if(x<=0)
		{
			printf("输入有误!\n");
			return 0;
		}
		return log10(x);
	}
	else if(strcmp(d,"sqrt")==0)
	{
		if(x<0)
		{
			printf("输入有误!\n");
			return 0;
		}
		return sqrt(x);
	}
	else if(strcmp(d,"floor")==0)return floor(x);
	else {
		printf("输入错误。\n请重新输入:\n");
		char a1[10000];
		memset(a1,'\0',sizeof(a1));
		get(a1);
		beforecal(a1);
	} 

	return -1;
}
bool isoperator(char ch)
{
	return (ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch=='^'||ch=='!');
}
int prior(char a){
	if(a=='+'||a=='-')return 1;
	if(a=='*'||a=='/')return 2;
	if(a=='^'||a=='!')return 3;
	return -1;
}//优先级赋值 

void rePolish(char* a)
{
	int paraf=0,para=0;//用来de sqrt((1+2)^)的bug 
	int judge2=0;//判断函数后面加没加括号 
	for(int i=0;i<strlen(a);i++)
	{
		if(isalpha(a[i])){
			cqueue[++ctail]=a[i];
			//printf("%c\n",cstack[ctop]);
			if(i!=strlen(a)-1&&((a[i+1]=='(')||isdigit(a[i+1])))cqueue[++ctail]=' ';//
		}
		if(isdigit(a[i]))
		{
			if(isalpha(a[i-1]))judge2=1;
			out[len++]=a[i];
			while(((a[i+1]=='.')||(isdigit(a[i+1])))&&i<strlen(a)-1)
			{
				out[len++]=a[++i];
			}//是数 就输出 注意小数  输出不用printf而是存到out数组里 
			out[len++]=' ';
			if(judge2){
				while(cfront!=(ctail+1)&&cqueue[cfront]!=' ')
				{
					out[len++]=cqueue[cfront++];
				}
				cfront++;
				out[len++]=' ';
				judge2=0;
			}//sin等函数后面的输入不加括号时 
		}
		if(a[i]=='('){
			stack[++top]=a[i];//左括号入栈 top要和指的地方一致  
			if(i!=0&&isalpha(a[i-1]))paraf++;		
			para++;
		}
		if(a[i]==')'){
			while(top&&stack[top]!='(')
				out[len++]=stack[top--];
			top--;
			out[len++]=' ';
			int judge3=0;//cfront++的判断 
			while(paraf==para&&cfront!=(ctail+1)&&cqueue[cfront]!=' ')
				{
					out[len++]=cqueue[cfront++];
					judge3=1;
				}
				if(judge3){
					cfront++;//越过空格
					judge3=0;
					paraf--;
				} 
			out[len++]=' ';	
			para--;
		}//右括号不入栈 一直输出直到遇到左括号 栈顶top再减一下跳过左括号 
		
		while(isoperator(a[i])) //遇到操作符 如果优先级比栈顶的低 要把前面的优先级也高的全部弹出 因此必须用while 
		{
			if(prior(a[i])>prior(stack[top])||top==0||stack[top]=='(')
			{
	  		        stack[++top]=' ';//添上这句逆波兰表达式会变丑 但是bug修复了 
					stack[++top]=a[i];//栈空或者优先级高就入栈 当前栈顶是左括号也要入栈   
					break;//需要break掉 while下一轮循环 
				}
			
			else{
				out[len++]=stack[top--];//优先级低就出栈 有括号也是这个效果 
				out[len++]=' ';
			}	
		}
	}
	//	printf("%s",cqueue);
	//	printf("%s",out);
	while(top)
	{
		out[len++]=stack[top--];
		out[len++]=' ';	
	}//弹出栈里剩余的东西 	
}

void beforecal(char *a)
{
	int lapp=0,rapp=0,k=0;
	int judge=0;
	char b[10000];
	for(int i=0;i<strlen(a);i++)//预处理 
	{
		if(a[i]==' ')continue;
		b[k++]=a[i];
		if(a[i]=='(')lapp++;
		if(a[i]==')')rapp++;
	//printf("%s\n",b);
		if((isdigit(a[strlen(a)-1])||a[strlen(a)-1]=='!'||a[strlen(a)-1]==')')&&(isdigit(a[i])||isalpha(a[i])||a[i]=='!'||isoperator(a[i])||a[i]=='('||a[i]==')'||a[i]=='.'));
		else {
			judge=1;
			break;
		}
	}
	if(lapp!=rapp)judge=1;
	if(judge==0)rePolish(b);
	else {
		printf("输入错误。\n请重新输入:\n");
		// char a1[10000];
		get(a);
		//printf("%s",a1); a1换成a就会出bug memset了也不行 
		beforecal(a);
	}
}
double cal(double x,double y,char ch)
{
	if(ch=='+')return x+y;
	if(ch=='-')return x-y;
	if(ch=='*')return x*y;
	if(ch=='/')return x/y;
	if(ch=='^')return pow(x,y); 

	return -1;
}
double calculate(char *a)
{
	char d[10000];//定义一个字符数组 存数和操作符 
	for(int i=0;a[i];i++)
	{
		memset(d,'\0',sizeof(d));
		if(a[i]!=' '){
			sscanf(a+i,"%s",d);// 利用前面打出的空格区分数 用sscanf读入一串数或者操作符到d里面 关键 
			if(isdigit(d[0]))
				dstack[++dtop]=atof(d);//atof将字符转化成double char类型的123.456变成了double型的123.456000 
			else{
				if(d[0]=='!'){
					if(dstack[dtop]-(int)dstack[dtop]!=0)
					{
						printf("输入错误。\n请重新输入:\n");
						// memset(a,'\0',sizeof(a));
						get(a);
						beforecal(a);
						return 0;
					}
					dstack[dtop]=(double)f1((int)dstack[dtop]);//单目 对栈顶操作就行 
					continue;//操作完继续读取 不continue会出bug 
				//	printf("%lf\n",dstack[dtop]);
				}
				if(isalpha(d[0]))
			        dstack[dtop]=f2(d,(double)dstack[dtop]);
				else{
					dstack[dtop-1]=cal(dstack[dtop-1],dstack[dtop],d[0]);//双目运算的话 计算栈顶的两个数 
					dtop--;
				}
			}
		}
		while(a[i]&&a[i]!=' ')i++;//跳过转换的那一段 	
	}
	return dstack[dtop];
}

void scan_mode(void)
{
	char mode=scan_char();
	// scanf("%c",&mode);
	if(mode=='1')
	{
		printf("请输入要计算的式子:\n");
		get(in);
		beforecal(in);
		//printf("%s\n",out);
		printf("%lf\n",calculate(out));
	}
	else if(mode=='2')
	{
		while(1)
		{
			printf("请输入要计算的式子:\n");
			get(in);
			beforecal(in);
			//printf("%s\n",out);
			printf("%lf\n",calculate(out));
		}
	}
	else{
		printf("模式输入有误,请重新输入: 1--单次计算, 2--多次计算\n");
		scan_mode();
	}
}
int main()
{
	printf("这里是一个计算器。\n它功能强大,你可以用它来计算阶乘、三角函数、反三角函数、双曲函数、自然对数、常用对数等等。\n"); 
	printf("选择模式: 1--单次计算, 2--多次计算\n");

	scan_mode();
	return 0;
}




           

晚上写物理实验报告算不确定度时用计算器试了一下,结果出bug了。。。

一番思索和调试后总算修复了!

大作业:计算器
大作业:计算器

以后就计算器就用它了~