题解 | 简单计算器
简单计算器
https://www.nowcoder.com/practice/b8f770674ba7468bb0a0efcc2aa3a239
#include <stdio.h>
#include <math.h> // fabs 函数需要 math.h
// 将 sentence[n..j] 这一段字符串转换成数字数组 num 中的各位数字
// 同时返回小数点在原始字符串中的下标;如果没有小数点,则默认在 j+1(即“小数点在最后一位数字之后”)
int convert(int n, int j, char sentence[51], int num[50])
{
// 默认认为小数点在 j+1(代表“没有小数部分”)
int num_of_point = j + 1;
// 遍历 [n, j] 区间,把数字字符存成对应的数值
for (int i = n; i <= j; i++)
{
if (sentence[i] != '.')
{
// '0' 的 ASCII 是 48,这里把字符 '0'~'9' 转成 0~9
num[i] = sentence[i] - '0';
}
else
{
// 记录小数点位置,后面按它把整数部分和小数部分分开
num_of_point = i;
}
}
return num_of_point;
}
// 计算整数部分的值:
// 已知小数点位置 num_of_point,整数部分是 [n .. num_of_point-1] 这一段
// 从右往左累加:个位 *1,十位 *10,百位 *100 ...
double value_bigger(int num_of_point, int num[50], int n)
{
int status = 1; // 当前位对应的权值(1, 10, 100, ...)
double value = 0;
for (int i = num_of_point - 1; i >= n; i--)
{
value += num[i] * status;
status *= 10;
}
return value;
}
// 计算小数部分的值:
// 已知小数点位置 num_of_point,小数部分是 [num_of_point+1 .. n] 这一段
// 从左往右累加:第一位 *0.1,第二位 *0.01,第三位 *0.001 ...
double value_smaller(int num_of_point, int num[50], int n)
{
double status = 0.1; // 当前位对应的权值(0.1, 0.01, 0.001, ...)
double value = 0;
for (int i = num_of_point + 1; i <= n; i++)
{
value += num[i] * status;
status /= 10;
}
return value;
}
int main()
{
// sentence 用来存放输入的整行表达式,比如 "12.34+56.78"
char sentence[51] = {0};
scanf("%s", sentence); // 这里按题目要求用 %s,遇到空格会截断
// num_1 复用来存放左右两个数的各位数字(按原始下标存)
int num_1[50] = {0};
char ch = '\0'; // 运算符 (+ - * /)
int j = 0; // 运算符在字符串中的下标
int end = 0; // 字符串长度(不包括 '\0')
double value_of_big1, value_of_big2; // 两个数的整数部分
double value_of_small1, value_of_small2; // 两个数的小数部分
double value1, value2; // 两个完整的 double 数值
// 先扫描整条字符串:
// 1. 统计长度 end
// 2. 找到运算符及其位置 j
// 3. 检查是否有非法字符
for (int i = 0; sentence[i] != '\0'; i++)
{
end++;
// 判断是否是运算符
if (sentence[i] == '+' || sentence[i] == '-' ||
sentence[i] == '*' || sentence[i] == '/')
{
ch = sentence[i];
j = i;
}
// 不是运算符的话,只允许数字和小数点
else if (sentence[i] != '.' && (sentence[i] < '0' || sentence[i] > '9'))
{
printf("Invalid operation!");
return 0;
}
}
// 如果扫描完都没找到运算符,表达式不合法
if (ch == '\0')
{
printf("Invalid operation!");
return 0;
}
// 解析第一个数:在 [0 .. j-1] 这一段
int num_of_point1 = convert(0, j - 1, sentence, num_1);
// 解析第二个数:在 [j+1 .. end-1] 这一段
int num_of_point2 = convert(j + 1, end - 1, sentence, num_1);
// 分别计算两个数的整数部分
value_of_big1 = value_bigger(num_of_point1, num_1, 0);
value_of_big2 = value_bigger(num_of_point2, num_1, j + 1);
// 分别计算两个数的小数部分
value_of_small1 = value_smaller(num_of_point1, num_1, j - 1);
value_of_small2 = value_smaller(num_of_point2, num_1, end - 1);
// 合并整数和小数部分,得到真正的 double 值
value1 = value_of_big1 + value_of_small1;
value2 = value_of_big2 + value_of_small2;
// 除法时需要判断除数是否为 0(这里用一个很小的阈值判断“接近 0”)
if (ch == '/' && fabs(value2) < 1e-9)
{
printf("Wrong!Division by zero!");
return 0;
}
double result = 0;
// 根据运算符执行对应的四则运算
switch (ch)
{
case '+':
result = value1 + value2;
break;
case '-':
result = value1 - value2;
break;
case '*':
result = value1 * value2;
break;
case '/':
result = value1 / value2;
break;
default:
// 理论上不会走到这里,前面已经检查过运算符是否合法
printf("Invalid operation!");
return 0;
}
// 按题目要求输出:保留 4 位小数,格式为 a?b=result
printf("%.4lf%c%.4lf=%.4lf", value1, ch, value2, result);
return 0;
}

