翁恺老师的计算器大作业~
改掉了逆波兰表达式的一些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了。。。
一番思索和调试后总算修复了!
以后就计算器就用它了~