算术表达式求值
问题描述:编写程序,计算算术表达式串的值,具体要求如下:
① 表达式串在运行时输入。
② 表达式串支持+、-、、/(精确除) 、%(整除取余)、圆括号等运算符,且支持任意位数的整 形常量和浮点型常量。如“33/2-(41.23+2)(52%7)”的值为“-113.19”。
③ 运算符优先级依次为:括号、乘除、加减,若优先级相同,则从左至右。
④ 当表达式串非法时,能提示信息。 涉及算法及知识:栈的应用、任意位数的整数/浮点数字符串转换为对应的整数/浮点数。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<math.h>
#define MaxSize 100
typedef struct CharStack //字符栈
{
char data[MaxSize];
int top;
}cStack;
typedef struct DoubleStack //数据栈
{
double data[MaxSize];
int top;
}dStack;
//字符栈初始化
void Initc(cStack* s1)
{
s1->top = -1;
}
//字符进栈
int Pushc(cStack* c1, char op)
{
if (c1->top < MaxSize)
{
c1->data[++c1->top] = op;
return 1;
}
else return 0;
}
//寻找栈顶元素
char Gettopc(cStack* c1)
{
return c1->data[c1->top];
}
//字符出栈
char Popc(cStack* c1)
{
return c1->data[c1->top--];
}
//初始化数据栈
void Initd(dStack* d1)
{
d1->top = -1;
}
//数据进栈
int Pushd(dStack* d1, double data)
{
if (d1->top < MaxSize)
{
d1->data[++d1->top] = data;
return 1;
}
else return 0;
}
//数据出栈
double Popd(dStack* d1)
{
return d1->data[d1->top--];
}
//当前扫描运算符优先级
int Inthis(char op)
{
switch (op)
{
case '(': return 6;
case '+': case '-': return 2;
case '*': case '/':case '%': return 4;
}
}
//栈顶扫描运算符优先级
int Intop(char op)
{
switch (op)
{
case '(': return 1;
case '+': case '-': return 3;
case '*': case '/':case '%': return 5;
}
}
//转化字符串
void Trans(char* s1, char* s2)
{
int i = 0;
int j = 0;
int f1 = -1;
//f1为0表示上次输出为数字,f1为1表示上次输出为字符
int f2 = -1;
//f2为0表示上次扫描为数字,f2为1表示上次扫描为运算符,用于区分数字后加空格
cStack string;
Initc(&string);
while (s1[i] != '\0')
//处理负数
{
if (s1[0] == '-')
//第一位数字为负数时
{
j = strlen(s1);
while (j > 0)
{
s1[j + 5] = s1[j];
j--;
}
s1[j++] = '(';
s1[j++] = '0';
s1[j++] = '-';
s1[j++] = '1';
s1[j++] = ')';
s1[j] = '*';
}
if (s1[i] == '(' && s1[i + 1] == '-')
//非第一位负数时
{
j = strlen(s1);
while (j > i + 1)
{
s1[j + 5] = s1[j];
j--;
}
s1[j++] = '(';
s1[j++] = '0';
s1[j++] = '-';
s1[j++] = '1';
s1[j++] = ')';
s1[j] = '*';
i = i + 5;
}
i++;
}
i = 0;
j = 0;
while (s1[i] != '\0')
{
if (f1 == 0 && f2 == 1)
//若上次的输出为数字,上次循环扫描为字符,则表示该数字串结束,则在数字后加空格区分
{
s2[j++] = ' ';
f1 = 1;
}
if ((s1[i] >= '0' && s1[i] <= '9') || s1[i] == '.')
{
s2[j++] = s1[i];
f2 = 0;
f1 = 0;
}
else if (s1[i] == '+' || s1[i] == '-' || s1[i] == '*' || s1[i] == '/' || s1[i] == '%' || s1[i] == '(')
{
f2 = 1;
if (string.top<0 || Inthis(s1[i])>Intop(Gettopc(&string)))
{
Pushc(&string, s1[i]);
}
else
{
while (string.top >= 0 && Inthis(s1[i]) < Intop(Gettopc(&string)))
//当前扫描字符优先级不断与栈顶字符优先级比较,当前字符小于栈顶字符时退栈并输出
{
s2[j++] = Popc(&string);
f1 = 1;
}
if (string.top<0 || Inthis(s1[i])>Intop(Gettopc(&string)))
//当前字符优先级大于栈顶优先级或栈空时当前字符压入字符栈内
{
Pushc(&string, s1[i]);
}
}
}
else if (s1[i] == ')')
{
f2 = 1;
if (Gettopc(&string) != '(')
//若括号仅包含数字则没有输出运算符
{
f1 = 1;
}
while (Gettopc(&string) != '(')
{
s2[j++] = Popc(&string);
}
Popc(&string);
}
i++;
}
while (string.top >= 0)
//将栈内剩余的运算符依次退栈输出
{
s2[j++] = Popc(&string);
}
s2[j] = '\0';
}
void check(char* s2, int pd)
{
int i, j;
i = 1;
j = 0;
while (s2[i] != '\0')
{
if (s2[i] == '(')
{
if (s2[i - 1] != '+' || s2[i - 1] != '-' || s2[i - 1] != '*' || s2[i - 1] != '/' || s2[i - 1] != '%' || s2[i - 1] != '(')
{
printf("error!表达式格式出错!\n");
pd = 0;
break;
}
}
i++;
}
while (s2[j] != '\0')
{
if (s2[j] == ')')
{
if (s2[j + 1] != '+' || s2[j + 1] != '-' || s2[j + 1] != '*' || s2[j + 1] != '/' || s2[j + 1] != '%' || s2[j + 1] != ')')
{
printf("error表达式格式错误!\n");
pd = 0;
break;
}
}
j++;
}
}
//表达式求值
double Calculate(char* s2, double* pd2)
{
int i = 0;
int f;
double data1, data2;
double sum;
double exo;
dStack ds1;
Initd(&ds1);
while (s2[i] != '\0')
{
if (s2[i] == '+' || s2[i] == '-' || s2[i] == '*' || s2[i] == '/' || s2[i] == '%')
//若为运算符获取栈顶两个元素进行计算
{
data1 = Popd(&ds1);
data2 = Popd(&ds1);
if (s2[i] == '+') Pushd(&ds1, data2 + data1);
else if (s2[i] == '-') Pushd(&ds1, data2 - data1);
else if (s2[i] == '*') Pushd(&ds1, data2 * data1);
else if (s2[i] == '/')
{
if (data1 == 0)
{
printf("\nerror!分母不能为零!\n");
*pd2 = 0;
}
else
Pushd(&ds1, data2 / data1);
}
else if (s2[i] == '%')
{
if (data1 == 0)
{
printf("\nerror!分母不能为零!\n");
*pd2 = 0;
}
else if (fmod(data2, 1) != 0)
{
printf("\nerror!整除求余不能出现小数!\n");
*pd2 = 0;
}
else if (fmod(data1, 1) != 0)
{
printf("\nerror!整除求余不能出现小数!\n");
*pd2 = 0;
}
else
Pushd(&ds1, fmod(data2, data1));
}
}
else
{
f = 0;
//初始化为0为整数部分标记,1为小数部分标记 //初始化为0为整数部分标记,1为小数部分标记
sum = 0;
exo = 1;
while (s2[i] != ' ' && s2[i] != '+' && s2[i] != '-' && s2[i] != '*' && s2[i] != '/' && s2[i] != '%')
{
if (s2[i] == '.')
//若有小数点,进入小数转化模式//若有小数点,进入小数转化模式
{
f = 1;
i++;
continue;
}
if (f == 0)
{
sum = sum * 10 + (double)(s2[i] - '0');
}
else
{
exo = exo * 10;
sum = sum + ((double)(s2[i] - '0')) / exo;
}
i++;
}
if (s2[i] == '+' || s2[i] == '-' || s2[i] == '*' || s2[i] == '/' || s2[i] == '%') i--;
//转化成功一个数据,若下个字符为运算符,则i--,回到当前运算的数据位置
Pushd(&ds1, sum);
}
i++;
}
return Popd(&ds1);
}
void main()
{
double end;
char s1[MaxSize];
//用于存储前缀表达式
char s2[MaxSize];
//用于存储后缀表达式
printf("*******请输入表达式*******\n\n");
while (1)
{
int pd1 = 1;
double* pd2 = 1;
scanf("%s", s1);
Trans(s1, s2);
//处理字符串,并转化为后缀表达式,存放在s2中
check(s2, pd1);
end = Calculate(s2, &pd2);
//后缀表达式求值//后缀表达式求值
if (pd1 == 0 || pd2 == 0)
printf("\n");
else if (pd1 == 1 || pd2 == 1)
printf("\n计算结果为: %.6f(结果保留六位小数)\n\n", end);
}
}