PAT乙级 舍入
作者 陈越
单位 浙江大学
不同的编译器对浮点数的精度有不同的处理方法。常见的一种是“四舍五入”,即考察指定有效位的后一位数字,如果不小于 5,就令有效位最后一位进位,然后舍去后面的尾数;如果小于 5 就直接舍去尾数。另一种叫“截断”,即不管有效位后面是什么数字,一概直接舍去。还有一种是“四舍六入五成双”,即当有效位的后一位数字是 5 时,有 3 种情况要考虑:如果 5 后面还有其它非 0 尾数,则进位;如果没有,则当有效位最后一位是单数时进位,双数时舍去,即保持最后一位是双数。
本题就请你写程序按照要求处理给定浮点数的舍入问题。
输入格式:
输入第一行给出两个不超过 100 的正整数 N 和 D,分别是待处理数字的个数和要求保留的小数点后的有效位数。随后 N 行,每行给出一个待处理数字的信息,格式如下:
指令符 数字
其中指令符
是表示舍入方法的一位数字,1
表示“四舍五入”,2
表示“截断”,3
表示“四舍六入五成双”;数字
是一个总长度不超过 200 位的浮点数,且不以小数点开头或结尾,即 0.123
不会写成 .123
,123
也不会写成 123.
。此外,输入保证没有不必要的正负号(例如 -0.0
或 +1
)。
输出格式:
对每个待处理数字,在一行中输出根据指令符处理后的结果数字。
输入样例:
7 3
1 3.1415926
2 3.1415926
3 3.1415926
3 3.14150
3 3.14250
3 3.14251
1 3.14
输出样例:
3.142
3.141
3.142
3.142
3.142
3.143
3.140
题目分析:
题目看上去很简单,但是有非常多的细节需要处理,题目中说到“输入保证没有不必要的正负号(例如 -0.0
或 +1
)”,这句话其实是在提醒我们需要对负数进行特殊处理,并且输出的时候可能会出现不必要的负号。具体细节请看源代码中的注释。
Python3
def fun1(s,cnt): # 截取
zs,xs = s.split(".")
return zs + '.' + xs[:cnt]
def fun2(s,cnt): # 进位
zs,xs = s.split(".")
xs = str(int(xs[:cnt])+1)
if len(xs) > cnt:
zs = str(int(zs)+1) if int(zs) >= 0 else str(int(zs)-1) # 注意负数的时候整数部分应该减一,测试点7
xs = "0" * cnt
elif len(xs) < cnt: # 把原先的前导0补回来,测试点7
xs = "0" * (cnt - len(xs)) + xs
return zs + '.' + xs
n, d = map(int,input().split())
for i in range(n):
op, num = input().split()
op = int(op)
if "." not in num: # num是整数 测试点2,6
print(num+'.'+"0"*d)
continue
zs,xs = num.split(".")
if len(xs) <= d: # num的小数位数本身就小于等于d
print(num+'0'*(d-len(xs)))
else:
if op == 1:
if int(xs[d]) < 5:
res = fun1(num,d)
else:
res = fun2(num,d)
elif op == 2:
res = fun1(num,d)
elif op == 3:
# 有效位的后一位小于5 或 (有效位的有一位是5 且 5后面没有其它非零尾数 且 有效位的前一位数字是偶数)
if int(xs[d]) < 5 or (int(xs[d]) == 5 and (len(xs) == d + 1 or int(xs[d+1:]) == 0) and int(xs[d-1]) % 2 == 0):
res = fun1(num,d)
else:
res = fun2(num,d)
if eval(res) == 0 and res[0] == '-': # 注意舍入后出现-0的情况,测试点8
res = res[1:]
print(res)
C++
根据上述python代码由GPT生成,测试点7运行时错误
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
// 截取函数
string fun1(string s, int cnt) {
size_t dot_pos = s.find('.');
string zs = s.substr(0, dot_pos); // 整数部分
string xs = s.substr(dot_pos + 1); // 小数部分
return zs + '.' + xs.substr(0, cnt);
}
// 进位函数
string fun2(string s, int cnt) {
size_t dot_pos = s.find('.');
string zs = s.substr(0, dot_pos); // 整数部分
string xs = s.substr(dot_pos + 1); // 小数部分
// 取小数的前cnt位,并将它转为整数后加1
int carry = stoi(xs.substr(0, cnt)) + 1;
xs = to_string(carry);
if (xs.length() > cnt) { // 进位到整数部分
if (zs[0] == '-') { // 处理负数进位的情况
int zs_int = stoi(zs);
zs_int -= 1; // 负数情况下,整数部分减1
zs = to_string(zs_int);
} else {
int zs_int = stoi(zs);
zs_int += 1; // 正数情况下,整数部分加1
zs = to_string(zs_int);
}
xs = string(cnt, '0'); // 小数位补零
} else if (xs.length() < cnt) { // 补回前导0
xs = string(cnt - xs.length(), '0') + xs;
}
return zs + '.' + xs;
}
int main() {
int n, d;
cin >> n >> d; // 读取n和d
for (int i = 0; i < n; ++i) {
int op;
string num;
cin >> op >> num; // 读取操作类型和数字
if (num.find('.') == string::npos) { // 整数情况
cout << num + '.' + string(d, '0') << endl;
continue;
}
size_t dot_pos = num.find('.');
string zs = num.substr(0, dot_pos); // 整数部分
string xs = num.substr(dot_pos + 1); // 小数部分
if (xs.length() <= d) { // 小数位数小于等于d
cout << num + string(d - xs.length(), '0') << endl;
} else {
string res;
if (op == 1) {
if (xs[d] < '5') {
res = fun1(num, d); // 截取
} else {
res = fun2(num, d); // 进位
}
} else if (op == 2) {
res = fun1(num, d); // 直接截取
} else if (op == 3) {
if (xs[d] < '5' || (xs[d] == '5' && (xs.length() == d + 1 || stoi(xs.substr(d + 1)) == 0) && (xs[d - 1] - '0') % 2 == 0)) {
res = fun1(num, d); // 截取
} else {
res = fun2(num, d); // 进位
}
}
// 处理舍入后的 -0 问题
if (stod(res) == 0 && res[0] == '-') {
res = res.substr(1); // 去掉负号
}
cout << res << endl;
}
}
return 0;
}