跳转至

PIO14 单组_保留小数位数

入门通过率 76.13%python3样例通过牛客 AC

牛客原题  源码

讲解章节输入输出处理浮点与科学计数法

一句话

读入一个小数,四舍五入保留 3 位后输出。

解题思路

这题考什么

输入形态:单组数据,一行一个小数。真正的难点不在读入,而在输出格式。

本题是严格比对(exact):判定程序把选手输出与标准答案逐字符对照, 完全一致才算通过(见 solutions/_judge.json 里 PIO14 的 mode 为 exact)。 所以「1.230」和「1.23」是两个不同的答案,前者对后者错; 末位差一个数字同样直接判错,没有误差容忍—— 这一点与 PIO17 那种带误差容忍的浮点判定截然相反。

题目要求的是数学意义上的四舍五入:末位小于 5 舍去,大于等于 5 进位。 Python 内建的 round() 和格式化都不是这个规则,所以这一题不能用它们。

为什么不能用 round() 或格式化

两个独立的问题叠在一起。

其一是舍入规则。Python 采用「四舍六入五成双」(banker's rounding, 银行家舍入:恰好落在半途时进到最近的偶数上,好处是大批数据求和时误差不累积)。 round(2.5) 得 2 而不是 3,round(0.5) 得 0,与题目要求相反。

其二是二进制表示。float 是 IEEE-754 双精度,十进制小数大多存不精确: 1.005 在内存里其实略小于 1.005,格式化到 2 位小数会得到 1.00。 也就是说,即使舍入规则改对了,先转成 float 这一步已经把信息弄丢了。 浮点误差的来源见 23-浮点与科学计数法

正确做法是全程不碰 float: Decimal 直接从字符串构造,十进制的位是什么就是什么,一位不差; quantize 指定保留到 0.000(即 3 位小数),舍入方式显式写成 ROUND_HALF_UP, 这才是题目要的四舍五入。

数据规模与复杂度

只有一个数,1 <= n <= 1e9。Decimal 按十进制位运算, 位数不超过输入长度,时间和空间都是常数级。

坑在哪

  1. 绝不能写 Decimal(float(s))。float() 那一步就已经引入了二进制误差, 再交给 Decimal 也救不回来。必须 Decimal(字符串)。
  2. quantize 的模板 Decimal("0.000") 决定了小数位数,三个 0 对应 3 位。 写成 Decimal("0.00") 就变成 2 位,严格比对下直接判错。
  3. 输入可能根本没有小数点(样例 3 给的是 123),Decimal 照样能构造, quantize 后自动补成 123.000,不需要特判。
  4. strip() 去掉行尾残留字符,否则 Decimal 会因为无法解析而抛 InvalidOperation,表现为运行时错误。
  5. quantize 的结果仍是 Decimal,print 时它的字符串形式已经带足 3 位小数, 不要再套一层 float() 或格式化,那会把刚保住的精度重新丢掉。

样例复核

1.23 补零得 1.230;114.514 位数正好,原样输出; 123 无小数部分,补成 123.000。三个样例与题面一致。

参考实现

solutions/PIO14.py
1
2
3
4
5
6
from decimal import Decimal, ROUND_HALF_UP

# 从字符串构造 Decimal,全程不经过 float,避免二进制表示误差
n = Decimal(input().strip())
# 模板 "0.000" 决定保留 3 位;ROUND_HALF_UP 才是数学四舍五入,Python 默认是四舍六入五成双
print(n.quantize(Decimal("0.000"), rounding=ROUND_HALF_UP))
[:octicons-arrow-left-16: PIO13](PIO13.md) [PIO15 :octicons-arrow-right-16:](PIO15.md)