「四舍五入」是小学数学就学过的知识,也是日常计算中的常见需求。然而在 Python 编程中,实现真正符合直觉的四舍五入竟出人意料地复杂,网络上还充斥着各种不准确的教程。
假设有一个变量 a = 1.135,需要将其四舍五入保留两位小数,通常能找到两种方法:
第一种,使用 round 函数。第一个参数是原数字,第二个参数是要保留的小数位数。
round(a, 2)
运行结果是 1.14,看起来没问题。
第二种,通过格式说明符 .f 进行字符串格式化,在 f 前加上要保留的小数位数。这种方法在 % 格式化、format 方法和 f-string 中均适用。
print('%.2f' % a, '{:.2f}'.format(a), f'{a:.2f}')
输出结果同样是 1.14。
似乎这两种方法都能完美实现四舍五入……吗?
显然没这么简单。如果将 a 的值改为 1.125,再次运行上述代码,就会发现两种方法的结果都变成了 1.12,而非期望的 1.13。

千万别小看这微小的误差。实际场景中,例如计算成绩,这种偏差完全可能导致结果判定的不同。

一些不够深入的教程会解释说,这是因为 Python 采用了一种名为「四舍六入五成双」的舍入机制:当要舍去的数字是5时,看5前面的数字是奇数则进位,是偶数则舍去。因此 1.135 (奇数3) 得到 1.14,而 1.125 (偶数2) 得到 1.12。

还有些教程会提供一种“传统”方法来实现四舍五入:将数字乘以10的N次方,加上0.5后取整,再除以10的N次方。
int(a * 10 ** 2 + 0.5) / 10 ** 2
对 1.125 使用此方法确实得到了正确结果 1.13。
但问题到此就解决了吗?
如果再把 a 改为 1.035。你会发现结果变得不可预测:它既不符合奇数进位的规律,也未通过先乘再除的方法实现四舍五入。

为了探究规律,我们可以编写一个循环,测试从 1.005 到 1.995 的所有以 .005 结尾的数字。
for i in range(100):
s = f'1.{i:02}5'
a = float(s)
b = int(a * 100 + 0.5) / 100
print(a, round(a, 2), f'{a:.2f}', b, sep='\t')



观察输出结果可以发现:round 和字符串格式化得到的结果完全一致,并且没有明确的规律可循。而先乘后除法在大多数情况下符合四舍五入,但仍存在一些例外。
导致这种现象的根本原因,与经典的 0.1 + 0.2 != 0.3 问题一样,都源于浮点数的精度限制。由于二进制表示的限制,很多在十进制下以5结尾的小数,其内部的二进制表示值其实略小于5,因此按照“小于5则舍去”的规则,自然不会被进位。
输出更多小数位数后,就能清楚地看到这个现象:

那么,如何在 Python 中实现精确且可控的四舍五入呢?答案是使用内置的 decimal 模块,它专门用于高精度的十进制算术运算。
使用 round 函数处理 Decimal 类型对象,才真正实现了「四舍六入五成双」的银行家舍入法。
from decimal import Decimal
x = 1.035
print(round(Decimal(str(x)), 2))

这种「银行家舍入」机制其实比传统四舍五入更科学。因为5正好是两个相邻数字的中间值,如果全部进位,会导致统计上的整体偏差偏大。而银行家舍入规则让舍入误差在大量计算中相互抵消,趋向于零。
如果你坚持需要传统的“四舍五入”规则,同样可以通过 decimal 模块实现。只需将 Context 中的 rounding 属性设置为 ROUND_HALF_UP。
from decimal import Decimal, ROUND_HALF_UP, getcontext
x = 1.045
getcontext().rounding = ROUND_HALF_UP
print(round(Decimal(str(x)), 2))
另一种等效的写法是使用 Decimal 对象的 quantize 方法,直接指定保留位数和舍入规则。
from decimal import Decimal
x = 1.045
print(Decimal(str(x)).quantize(Decimal('0.01'), rounding='ROUND_HALF_UP'))
这样,就能完美地按照四舍五入规则保留小数了。
但这里还有一个至关重要的细节:必须通过字符串来创建 Decimal 对象。如果直接用浮点数初始化,该浮点数本身携带的精度误差会传入 Decimal,从而导致舍入规则再次失效。

总结一下,在 Python 中处理金融、科学计算等对精度要求高的四舍五入时,最可靠的方法是结合 decimal 模块与字符串输入。理解这一机制,是深入 计算机基础 中数值计算原理的重要一步。希望这篇解析能帮助你彻底避开这个看似简单却暗藏玄机的“坑”。