跳转至

PIO17 单组_spj判断浮点误差

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

牛客原题  源码

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

一句话

给定整数半径 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)。

坑在哪

  1. 自己写近似的 pi 会超出误差容忍,这是本题最典型的失分原因。
  2. 输出位数太少同样会超出容忍,至少要 4 位小数。
  3. 半径是整数,但面积不是;int(input()) 读半径没问题, 计算结果不要再取整,那会引入远大于 1e-3 的误差。

参考实现

solutions/PIO17.py
1
2
3
4
5
import math

r = int(input())
# 输出 6 位小数:截断误差约 5e-7,远小于题目容忍的 1e-3,同时不超出双精度的有效位数
print("%.6f" % (math.pi * r * r))
[:octicons-arrow-left-16: PIO16](PIO16.md) [PIO18 :octicons-arrow-right-16:](PIO18.md)