网上一查很多资料都说 Python 内置的 round()
是用于四舍五入的函数,其实它包含着很大的坑,本文将会说明。
说明
先上几个正常的例子熟悉下用法:
1 | # Python 3.7.0 |
再上几个打脸的例子:
1 | # Python 3.7.0 |
纳尼?小数部分的 0.5 为啥被舍掉了,不应该“五入”进一吗?查阅官方文档可知, round()
函数有两个参数,依次是:
number
: 整数或浮点数ndigits
(可选):整数。若被省略或为 None 则函数返回整数,否则函数返回值的类型与 number 相同。
函数的作用为:
Return number rounded to ndigits precision after the decimal point.
翻译过来不就是四舍五入的意思吗?仔细往下看文档,特殊场景下不是的:
if two multiples are equally close, rounding is done toward the even choice.
这句话书上的解释是:
“当某个值恰好等于两个整数间的一半时,取整操作会取到离该值最接近的那个偶数上”
——《Python Cookbook 中文版第 3 版》 P83
也就是说,判断进位的数恰好是 5 的情况,要看 5 的前一位是奇数还是偶数,如果是奇数,则进一,例如 round(1.5)
的结果是 2 ;如果是偶数,则不会进一,而是直接把 5 舍掉,例如 round(2.5)
的结果也是 2 。以上举的几个例子都是浮点数,不过仔细看文档和书上对 round()
函数第二个参数 ndigits 的说明,会发现:
“传递给
round()
的参数 ndigits 可以是负数,在这种情况下会相应地取整到十位、百位、千位等”
——《Python Cookbook 中文版第 3 版》 P83
也就是说,给第二个参数 ndigits 传入 -1, 会取整到十位,传入 -2,会取整到百位,以此类推。以取整到十位为例,示例如下:
1 | # Python 3.7.0 |
如果你想:我对判断进位的数是 5 的情况特殊处理,还是可以达到四舍五入的目的的。那么请再看下面的示例:
1 | # Python 3.7.0 |
Excuse me??? 不是说 5 的前一位是奇数的话要进一么,怎么又不对了?难道是 Python 的 Bug ? Python 官方文档说这不算 Bug ,而是浮点数固有的精度丢失问题。我们都知道,计算机存储的任何数据其实都是二进制流,也就是一串 0 和 1。整数在计算机内是可以精确存储的,而大多数浮点数则不然。因为大多数浮点数若用二进制表示,会变得很长甚至无限长,计算机需要将其截断才能存储,这样就丢失了精度。就拿 2.675 来说,它要存储到计算机里,首先要转为二进制,结果为:10.1010110011001100(假设计算机截断小数点后十六位),如下图所示:

我们把计算机存储 2.675 的这个二进制串再转回十进制,就变成了:2.67498779296875 ,如下图所示:

所以, round(2.675, 2)
的结果就成了 2.67 。更详细的说明可以看参考文献 2
替代方案
既然 Python 四舍五入不可以用 round()
,那有没有替代方案呢?
当然有, decimal
模块就是用于浮点数的精确运算的,其中就包括四舍五入。用法示例如下:
1 | #!/usr/bin/env python3 |
运行这段代码,输出结果为 2.68
关于 decimal
模块的详细用法和相关问题,可以看参考文献 3
总结
Python 四舍五入不可以用 round()
,需要用到 decimal
模块。