原题:
3.7 计算加减表达式 | 语言要求: | C++ | ||
编写程序,使其能读入并计算一个只包含加减运算的表达式,每一个输入的数据都是浮点数,除第一个数以外, 其余每个数前面都有一个运算符,表达式以分号“;”结束。 例如: 23+43-233+234; 输入输出格式要求: 要求输出小数点后6位。 只输出运算结果,请不要输出其他字符,遇到异常情况(如结尾不是分号等),输出error 例如: 输入:23+43-233+234;回车 输出:67.000000 输入:23+43,回车 输出:error |
出现的困难:
从读入的字符串中提取浮点数,
当用“从前往后扫描数组用frac*=0.1处理小数部分”的方法时,
测试用例:12.345678;回车
输出:12.345677
回到vs2019中调试,在debug过程中跟踪放小数的变量float frac,发现其总是有“小尾巴”,即
double frac=0.1;有:
名称 | 值 | 类型 | |
---|---|---|---|
frac | 0.10000000000000001 | double |
查资料后发现以二进制储存数据的计算机 无法精度较高地储存输入的十进制浮点数。
解决方案:
①以整数存储integer和fraction。从前往后扫描,整数部分直接相加,小数部分存储下来。小数部分按最大位数数字后加0补齐位数,以整数部分相加。如最大位数为3,求和后第四位及以上位数的加到整数部分。输出的时候手动输出: cout<<integer<<"."<<fraction;如果小数部分是负数,则整数部分多减去1,再算(100...00-小数部分)。【也为高精度浮点数专门写一个浮点数类,其成员以分开整数部分和小数部分,以int类型存储。】
②直接使用大数加减法,但要以小数点对齐,末位补零。如12.345+5.78-9.123应该如下:
12 345
+5 780
-9 123
————
+8 997
其中空格为小数点,但可以用整数加减的方式运算。【大数加减那类题目】
测试用例:
输入:23+43-233+234;回车
输出:67.000000
输入:23+43,回车
输出:error
输入:12.345678;回车
输出:12.345678
输入:12.34+5.78-9.123;回车
输出:8.997
输入:12.3456789+1.9999999;回车
输出:14.345679
输入:34.2343+89.345687-12.32+5; 回车
输出:116.259987
(有小数点混杂无小数点)
代码:
#include<iostream>
#include<math.h>
#include<ctype.h>
using namespace std;//命名域声明要写在#include<iostream>后。。。
int main()
{
char str[50];
cin >> str;
int len = strlen(str);
if (str[len - 1] != ';')
{
cout << "error";
return 0;
}
int i;
int num = 0, sum = 0;
int sum_decloca = -1, num_decloca = -1;//记录小数点位置,即小数点后有几位数。-1代表还未激活
int sign = 1;
if (str[0] == '-')sign = -1;
for (i = 0; i < len; i++)
{
//合法性判断:
if (!isdigit(str[i]) && str[i] != '+' && str[i] != '-' && str[i] != '.' && str[i] != ';')
{
cout << "error";
return 0;
}
if (isdigit(str[i]))
{
num = num * 10 + str[i] - '0';
if (num_decloca != -1)num_decloca++;
}
else if (str[i] == '.')
{
num_decloca++;
}
else //如果是'+'或'-'或 ';'
{
if (sum_decloca == num_decloca || sum_decloca == -1)//sum_decloca==-1 demonstrates it is processing the first number
{
sum += sign * num;
if (sum_decloca == -1)sum_decloca = num_decloca;
}
else if (sum_decloca > num_decloca)
{
//当当前读取的数字num没有小数点:
if(num_decloca==-1)sum += sign * (double)num * pow(10, ((double)sum_decloca));//当前操作数扩大补零
//有小数点:
else sum += sign * (double)num * pow(10, ((double)sum_decloca - (double)num_decloca));//当前操作数扩大补零
sum = (int)sum;
num_decloca = sum_decloca;
}
else
{
sum = sum * (int)pow(10, ((double)num_decloca - (double)sum_decloca)) + sign * num;//将sum扩大补零
sum_decloca = num_decloca;
}
//当前的运算符是对下一个数字的,故sign放在加减操作之后
if (str[i] == '+')sign = 1;
else if (str[i] == '-')sign = -1;
//重置:
num = 0; num_decloca = -1;
}
}
if (sum_decloca == -1)//输入数字全是整型
{
cout << sum << ".000000";
}
else if (sum_decloca <=6)
{
cout << sum / (int)pow(10, (double)sum_decloca) << '.' << sum % (int)pow(10, (double)sum_decloca);
for (i = 1; i <= 6 - sum_decloca; i++)
{
cout << '0';
}
}
else if (sum_decloca > 6)
{
sum += 5 * (int)pow(10, (double)(sum_decloca - 6 - 1));//四舍五入
sum /= (int)pow(10, (double)(sum_decloca - 6));//去掉六位小数以后的数字
cout << sum / (int)pow(10.0,6.0) << '.' << sum % (int)pow(10.0,6.0);
}
return 0;
}
参考: