PIO17 单组_spj判断浮点误差¶
一句话
给定整数半径 r,求圆的面积。
解题思路¶
这题考什么¶
输入形态:单组数据,一行一个整数。题面输入描述里把这个数写作 n, 但按题目描述它是圆的半径,所以代码里叫 r。 考点是浮点结果的输出方式,以及带误差容忍的判定。
本题是浮点判定(float),容忍误差 1e-3 (见 solutions/_judge.json 里 PIO17 的 mode 为 float、eps 为 0.001)。 判定不是逐字符比对,而是把两边的数值读出来,判断差的绝对值是否在容忍范围内。
为什么浮点题不能直接相等比较¶
圆面积是 pi 乘 r 的平方,pi 是无理数,十进制展开无限不循环, 任何有限位数的输出都只是近似值——「正确答案」这个字符串根本不存在。 再往下一层,float 是 IEEE-754 双精度,只有约 15 到 16 位有效十进制数字, 而且乘法本身也要舍入;不同实现的运算顺序、库函数精度稍有差别, 末位就会不同。要求两个浮点数逐位相等,等于要求两套实现的舍入误差完全一致, 这是做不到的。所以浮点题一律改用「差的绝对值小于容忍值」来判定, 容忍值由题目给出(本题 1e-3)。相关讨论见 23-浮点与科学计数法。
为什么用 math.pi 而不是自己写 3.14159¶
math.pi 是双精度下最接近 pi 的值,有效数字约 16 位; 手写的 3.14159 只有 6 位有效数字,与真值相差约 2.65e-6。 这个偏差会被 r 的平方放大:r 取上界 1e3 时面积约 3.14e6, 绝对误差约 2.65e-6 乘 1e6,即约 2.65——比容忍的 1e-3 大了三个数量级, 必定判错。误差容忍是给舍入用的,不是给偷工减料的常数用的。
为什么输出 6 位小数¶
输出位数本身也会引入误差,最大是末位的一半。 只保留 2 位小数时,这个截断误差最大 0.005,已经超过容忍的 1e-3, 可能被判错;保留 4 位小数时最大 5e-5,才安全。 取 6 位是留足余量:面积最大约 3.14e6,加上 6 位小数共约 13 位有效数字, 仍在双精度的 15 到 16 位之内,不会输出出双精度撑不住的假精度。 位数宁多勿少,多给几位不会错,少给几位会踩容忍线。
数据规模与复杂度¶
1 <= r <= 1e3,r 的平方最多 1e6,面积最大约 3.14e6,双精度绰绰有余。 时间和空间都是 O(1)。
坑在哪¶
- 自己写近似的 pi 会超出误差容忍,这是本题最典型的失分原因。
- 输出位数太少同样会超出容忍,至少要 4 位小数。
- 半径是整数,但面积不是;int(input()) 读半径没问题, 计算结果不要再取整,那会引入远大于 1e-3 的误差。
参考实现¶
| solutions/PIO17.py | |
|---|---|