可计算表达式中带有:负数、小数、不止是个位数
-99.12*(3.2+(-0.65))/2+2^3#
(-3.125+(-0.125))*2/(-2)#
2/0#
作者说明
学习中,我发现网上没有找到计算表达式 较系统的解答。要么只能计算个位数,要么不能有小数点等等
难点说明
- 栈的基本操作
- 数字的提取 负数的处理 小数的处理
- 后缀表达式的理解
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define INITSIZE 100
#define STACK_INCREASE 10
typedef struct {//存字符
char *base;
char *top;
int stacksize;
}Stack_1; //符号位
typedef struct {//存数字
float *base;//double当然也可以
float *top;
int stacksize;
}Stack_2; //数字位
Stack_1 S; //声明在这里,为函数传值更有效
Stack_2 S2;
int InitStack(Stack_1 &S,Stack_2 &S2){//一起初始化
S.base=(char *)malloc(INITSIZE*sizeof(char));
S2.base=(float *)malloc(INITSIZE*sizeof(float));
if(!S.base||!S2.base){
printf("初始化失败!");
exit(0);
}
S.top=S.base;
S.stacksize=INITSIZE;
S2.top=S2.base;
S2.stacksize=INITSIZE;
}
int GetTop_1(Stack_1 S,char &e){//取栈顶运算符号
if(S.base==S.top){
printf("栈空!");
return 0;
}
e=*(S.top-1);
return 1;
}
int GetTop_2(Stack_2 S2, float &e){//取栈顶数字
if(S2.base==S2.top){
printf("栈空!");
return 0;
}
e=*(S2.top-1);
return 1;
}
int Push_1(Stack_1 &S,char e){//入栈
if(S.top-S.base>=S.stacksize){
S.base=(char*)realloc(S.base,(S.stacksize+STACK_INCREASE)*sizeof(char));
if(!S.base){
printf("重新分配空间失败!");
exit(0);
}
S.top=S.base+S.stacksize;
S.stacksize+=STACK_INCREASE;
}
*S.top++=e;
return 1;
}
int Push_2(Stack_2 &S2,float e){//入栈
if(S2.top-S2.base>=S2.stacksize){
S2.base=(float*)realloc(S2.base,(S2.stacksize+STACK_INCREASE)*sizeof(float));
if(!S2.base){
printf("重新分配空间失败!");
exit(0);
}
S2.top=S2.base+S2.stacksize;
S2.stacksize+=STACK_INCREASE;
}
*S2.top++=e;
return 1;
}
int Pop_1(Stack_1 &S,char &e){//出栈
if(S.base!=S.top){
e=*--S.top;
}
else {
printf("栈空!");
}
}
int Pop_2(Stack_2 &S2,float &e){
if(S2.base-S2.top!=0){
e=*--S2.top;
}
else {
printf("栈空565555555!");//个人调试时标记的东东
}
}
int isStack_1(char ch){//确认是云算符
if(ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch=='('||ch==')'||ch=='^'){
return 1;
}
return 0;
}
int isNumber(char ch){//确认是数字 例:‘2’
if(ch>='0'&&ch<='9'){
return 1;
}
return 0;
}
int priority(char ch){ //priority和compare函数用来判断权大小
if(ch=='+'||ch=='-')
return 1;
else if(ch=='*'||ch=='/')
return 2;
else if(ch=='#')
return 0;
else if(ch=='(')
return 3;
else if(ch==')')
return -1;
}
int compare(char ch1,char ch2){//有点不规范,望理解
if(ch1==')'&&ch2=='('){
return -2;//"()"
}
if(ch1!=')'&&ch2=='('){
return 2;//"*)"
}
if(ch1=='('&&ch2=='('){//权大
return 2;
}
if(ch1=='^'){
return 4;
}
int a=priority(ch1); //调用在次
int b=priority(ch2);
if(a>b) return 1;//(*
else if(a<b) return -1;
else return 0;
}
void countAB(Stack_2 &S2,char e){//算栈顶的两个数值
float a,b;
Pop_2(S2,a);
Pop_2(S2,b);
switch(e){
case '^':{
a=powf(b,a);
break;
}
case '+':{
a=b+a;
break;
}
case '-':{
a=b-a;
break;
}
case '*':{
a=b*a;
break;
}
case '/':{
if(a==0){
printf("分母不能为 0!\n");
exit(0);
}
a=b/a;
break;
}
}
Push_2(S2,a);
}
void countSum(char ch[]){
InitStack(S,S2);
Push_1(S,'#');
int i=0;
char ePass;
while(ch[i]!='#'){
if(i!=0){
ePass=ch[i-1];
}
else{
ePass='#';
}
if(ch[i]=='-'&&(ePass=='('||ePass=='#')){//特殊情况,解决负数,例:(-2)
i++;
float x=ch[i]-'0';
int j=i+1;
while(isNumber(ch[j])){
x=x*10+ch[j]-'0';
j++;
}
if(ch[j]=='.'){
j++;
float count=1.0;
while(isNumber(ch[j])){
x=x+(ch[j]-'0')*powf(0.1,count);
count++;
j++;
}
}
x*=-1.0;
i=j;
Push_2(S2,x);
}
else if(isNumber(ch[i])){//是数字
float x=ch[i]-'0';
int j=i+1;
while(isNumber(ch[j])){
x=x*10+ch[j]-'0';
j++;
}
if(ch[j]=='.'){
j++;
float count=1.0;
while(isNumber(ch[j])){
x=x+(ch[j]-'0')*powf(0.1,count);
count++;
j++;
}
}
Push_2(S2,x);
i=j;
}
else if(isStack_1(ch[i])){//是运算符
char e;
GetTop_1(S,e);
int y=compare(ch[i],e);//**与上一个运算符比较权**
switch(y){
case -2:{//()
Pop_1(S,e);
break;
}
case 2:{//*(
Push_1(S,ch[i]);
break;
}
case 1:{//*+
Push_1(S,ch[i]);
break;
}
case 4:{//*+
Push_1(S,ch[i]);
break;
}
case -1:{//)+
/**
1.除去+
2.用+来stack_2
3.还必须是 )
*/
Pop_1(S,e);
countAB(S2,e);
i--;
break;
}
case 0:{
Pop_1(S,e);
countAB(S2,e);
i--;
break;
}
}
i++;
}
}
char e0;
Pop_1(S,e0);
//printf("%c\n",e0);
while(e0!='#'){
countAB(S2,e0);
Pop_1(S,e0);
}
float sum=0;
GetTop_2(S2,sum);
printf("结果:%.3f\n",sum);//为了美观,保留了3位,但是对于小数多的就有缺精度的可能
}
int main(){
char ch[50];
while(scanf("%s",ch)){//不断输入
countSum(ch);
}
return 0;
}
可运行结果
解释:
- 为什么要用后缀表达式:后缀表达式
- 这里的两个栈,一个存运算符,一个存数字
这里的计算后缀表达式是边算边求的,因为这里有可以计算超过个位的数,不好给出全部后缀表达式,但是计算是利用后缀表达式的结构的。例如:33+92#的后缀表达式是:3392+
但是33与9 连在一起了,不好分开,你可以加一个分隔符:33|9|2*2
但这里没有给出了,避免麻烦,就直接计算了。 - 作业要求是将整个代码分成如下工程分块,因为操作不难,表示母鸡可以 Q 846891884,所以这里就一个整个代码块了。
- 这里符号权的大小比较写的比较生涩,个人话,不太好。 运算权是可以规范化的,因为没懂,所以没有用二维数字表示,(留意)
- 测试:(-3.125+(-0.125))*2/(-2)#
后面的#是可以设计删除的,也很简单 - 其实最难弄的就是 void countSum(char ch[]);函数了。
嘿嘿、哈哈:
有很多注释的不清楚。同时:如果这能与大整数联合起来就好了。
下次说大整数,因为我做了一点。
第一次这样的文稿,有点不太会,望理解、指错。
加油!