python 中浮点数四舍五入的问题
admin
2023-06-27 13:23:53
0

昨天遇到一个问题,在 6.6045 保留三位小数时,使用 round() 函数进行计算,我们希望得到 6.605,然而:

round(6.6045, 3)
6.604

网上有人说,因为在计算机里面,小数是不精确的,例如 1.115 在计算机中实际上是 1.114999999999999991182,所以当你对这个小数精确到小数点后两位的时候,实际上小数点后第三位是 4,所以四舍五入,结果为 1.11.

这种说法,对了一半。

因为并不是所有的小数在计算机中都是不精确的。例如 0.125 这个小数在计算机中就是精确的,它就是 0.125,没有省略后面的值,没有近似,它确确实实就是 0.125.

但是如果我们在 Python 中运行:

round(0.125, 2)
0.12

为什么在这里四舍了?

还有更奇怪的,另一个在计算机里面能够精确表示的小数 0.375,我们来看看精确到小数点后两位是多少:

round(0.375, 2)
0.38

为什么在这里又五入了?

解析

因为在 Python3 里面,round 对小数的精确度采用了四舍六入五成双的方式。

如果你写过大学物理的实验报告,那么你应该会记得老师讲过,直接使用四舍五入,最后的结果可能会偏高,所以需要使用奇进偶舍的处理方法。

例如对于一个浮点数 a.bcd,需要精确到小数点后两位,那么就要看小数点后第三位:
•如果 d 小于 5,直接舍去
•如果 d 大于 5,直接进位
•如果 d 等于 5:
• ◦d 后面没有数据,且 c 为偶数,那么不进位,保留 c

• ◦d 后面没有数据,且 c 为奇数,那么进位,c 变成 (c + 1)

• ◦如果 d 后面还有非 0 数字,例如实际上小数为 a.bcdef,此时一定要进位,c 变成 (c + 1)

关于奇进偶舍,有兴趣的朋友可以在维基百科搜索这两个词条:数值修约和奇进偶舍。

所以,round 给出的结果如果跟设想的不一样,那么需要考虑两个原因:
1.你的这个小数在计算机中能不能被精确储存?如果不能,那么它可能并没有达到四舍五入的标准,例如 1.115,它的小数点后第三位实际上是 4,当然会被舍去。
2.如果你的这个小数在计算机中能被精确表示,那么,round 采用的进位机制是奇进偶舍,所以这取决于你要保留的那一位,它是奇数还是偶数,以及它的下一位后面还有没有数据。

回到最开始的问题,对于 6.6045 这个浮点数,我们在 Scheme 中查看一下它的精确形式:

(exact 6.6045)
3718002967371055/562949953421312

也就是说它是不能被精确储存的,大概表现为 6.60449999999999…的形式,因此四舍五入的时候得到了 6.604。

如何正确进行四舍五入

如果要实现数学上的四舍五入,那么就需要使用 decimal 模块。

具体用法参考官方文档:https://docs.python.org/zh-cn/3.7/library/decimal.html

其中 quantize 的函数原型和文档说明,提到了可以通过指定 rounding 参数来确定进位方式。如果没有指定 rounding 参数,那么会默认使用上下文提供的进位方式。

现在我们来查看一下默认的上下文中的进位方式是什么:

from decimal import getcontext
getcontext().rounding
'ROUND_HALF_EVEN'

ROUND_HALF_EVEN 实际上就是奇进偶舍,如果要指定真正的四舍五入,那么我们需要在 quantize 中指定进位方式为 ROUND_HALF_UP:

from decimal import Decimal, ROUND_HALF_UP
Decimal('0.125').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.13')

现在看起来一切都正常了。

有人可能会进一步追问一下,如果 Decimal 接收的参数不是字符串,而是浮点数会怎么样呢?

来实验一下:

Decimal(0.125)
Decimal('0.125')

那是不是说明,在 Decimal 的第一个参数,可以直接传浮点数呢?

我们换一个数来测试一下:

Decimal(11.245)
Decimal('11.2449999999999992184029906638897955417633056640625')

浮点数 11.245 和字符串’11.245’传进去以后的结果居然不一样。

我们继续在文档中寻找答案。
python 中浮点数四舍五入的问题

官方文档已经很清楚地说明了,如果你传入的参数为浮点数,并且这个浮点值在计算机里面不能被精确存储,那么它会先被转换为一个不精确的二进制值,然后再把这个不精确的二进制值转换为等效的十进制值。对于不能精确表示的小数,当你传入的时候,Python 在拿到这个数前,这个数就已经被转成了一个不精确的数了。所以虽然参数传入的是 11.245,但是 Python 拿到的实际上是 11.24499999999…

相关内容

热门资讯

“敏昂莱更依赖中国,但首访选了... 【文/观察者网 陈思佳】缅甸外交部近日宣布,缅甸总统敏昂莱将于5月30日至6月3日访问印度,这是他就...
沈伯洋为选举拥抱贪污犯陈水扁,... 海峡导报综合报道 陈水扁日前力挺民进党台北市长参选人沈伯洋,希望沈能成为继自己之后,第2位民进党籍台...
原创 神... 大家好,我是小川[太阳] 5月29日晚,神舟二十二号飞船稳稳降落东风着陆场,圆满完成超长在轨任务,三...
85岁院士李立浧发出豪言:“我... 庆祝2026年“全国科技工作者日”,广州举办主场活动暨“科学家故事会” 羊城晚报全媒体记者 李钢 “...
钱学森之子辟谣 近年来,与钱学森先生相关的一句话广为流传,成为了“热梗”。这句话就是“人再笨还学不会微积分吗”,但其...
越来越多城市推行有奖发票 作者 | 第一财经 陈益刊今年中央财政投入100亿元在50个试点城市开展有奖发票试点,而未纳入试点范...
创明申请甲醇增程器功率控制方法... 国家知识产权局信息显示,绵阳创明智能电池有限责任公司、深圳市创明新能源股份有限公司申请一项名为“甲醇...
原创 红... 这一到618活动红米手机就杀疯了,本身高配低价的手机再降价,一下子吸引了很多用户入手,在京东平台内,...
一所突然“消失”的学校,一个心... 2120万元的赔偿请求,最终仅判赔2.53万元。2026年2月10日,一审判决送达后,武敏只用3天就...
新华社:人工智能和能源加速“双... 新华社发文表示,中国石油“昆仑”大模型深度解析复杂地质构造和油气藏特征,助力油气勘探开发提质增效;南...