Python中round()函数四舍五入陷阱与精确处理方案

陈默
陈默
发布时间:2026-10-13 | 6人浏览过
原创

本文深入解析Python中round()函数在特定小数位下“遇5不进”的现象,揭示其底层二进制浮点存储误差及银行家舍入法机制。通过对比放大倍数法与decimal.Decimal模块,提供两种精确控制小数位四舍五入的解决方案,帮助开发者避免财务或科学计算中的精度丢失问题。

Python round()函数基础与异常现象

round()函数简介

根据菜鸟教程的描述,round()函数的主要功能是将浮点数x四舍五入并返回结果。


> round( x [, n] )

该函数接受参数x和可选参数n,两者均为数值表达式。返回值是x四舍五入后的结果;若指定了参数n,则保留n位小数;若省略n,则仅返回四舍五入后的整数部分。


>>> round(2.3)
2
>>> round(2.45, 1)
2.5

特殊情况

上述结果看似正确,但若使用2.675进行测试,结果却出乎意料:


>>> round(2.675, 2)
2.67

显然,这一结果不符合常规的四舍五入直觉。造成这种现象的原因在于:当round()函数只有一个参数时,它返回最接近的整数,行为类似四舍五入;当指定小数位数时,通常也遵循四舍五入规则,但在处理.5的情况时,规则变为:若待舍去位数前一位是奇数,则直接舍去;若是偶数,则向上进位。

浮点数精度损失与二进制存储原理

在项目开发中,小数位的四舍五入处理十分常见。近日有开发者提问:如何将1.325转换为1.33?

乍看之下,这似乎是一个简单问题,可以迅速解决:

我的初始回复如下:


round(1.315,2)

随后有开发者反馈:

他期望得到的结果是1.32,但实际打印输出却是1.31。看到这一结果时我也感到惊讶,于是亲自运行代码验证:

结果确实显示为1.31。

起初我并未多想,自认为逻辑无误。这也提醒我们,开发人员往往难以测试自己的代码。

因此,我开始深入排查原因,并通过以下代码进行讲解:


print(Decimal(1.325))

打印结果如下:

1.3249999999999999555910790149937383830547332763671875

大家可以看到,数值1.325在转换为二进制存储时存在精度损失。部分小数无法用二进制精确表示,这是导致问题的根本原因。

此时可能有读者疑问:为什么有些情况下.5能正常进位?能否解释其原理? 原理与上述情况一致,此处以5能进位为例进行说明:


print(round(1.145,2))
#打印结果
1.15

继续查看其二进制保存的具体值:


print(Decimal(1.145))
#打印结果 
1.145000000000000017763568394002504646778106689453125

现在大家应该明白了,round()函数本身并无错误,问题在于二进制存储值带来的微小误差。

精确四舍五入的两种解决方案

又有读者提出:如何避免这种错误?

为此我准备了两种解决方案。

方案一:将数值放大100倍,利用下方代码实现精确的四舍五入结果。


def round_up(value):
  # 替换内置round函数,实现保留2位小数的精确四舍五入
  return round(value * 100) / 100.0

应用该方法查看结果:


def round_up(value):
  # 替换内置round函数,实现保留2位小数的精确四舍五入
  return round(value * 100) / 100.0

print(round(1.4))

print(round(1.5))

print(round_up(1.115))

print(Decimal(1.115))

#打印结果:
1
2
1.12
1.1149999999999999911182158029987476766109466552734375

效果看起来不错,1.115成功转换为了1.12。

方案二:使用decimal.Decimal。

四舍五入基于十进制运算,当二进制无法精确表示时必然产生误差。

因此,任何涉及十进制运算的场景,都应使用decimal.Decimal替代float类型。


from _pydecimal import Decimal, Context, ROUND_HALF_UP
print(Context(prec=3, rounding=ROUND_HALF_UP).create_decimal('1.325'))

打印结果如下:

1.33

大家可以尝试其他数值,均能实现正确的进位。

通过上述讲解,大家应已明白:round()函数本身没有问题,只是float类型在存储过程中存在误差。

银行家舍入法机制与推荐实践

也可以这样解释:

这并非bug,而是一种常见的舍入方法,称为“银行家式舍入法”(Banker's Rounding)。

其设计意图是“一半舍去,一半进入”。如果遇到0.5一律进位,银行可能会觉得吃亏。

银行希望将风险均匀分配给用户和自己。不仅Python,其他计算机语言也采用此方法。

例如C语言和Basic语言。事实上,不仅在计算机科学领域,在科学实验的数据处理中,也广泛采用这种舍入法。

推荐第二种方法。细心的读者可以发现,第一种方法仍无法完全实现所有情况下的“5进位”,大家可以自行思考其局限性。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表