在Python里想要四舍五入有多麻烦?

本文揭示了Python中四舍五入操作的复杂性,特别是在处理浮点数精度问题时。作者介绍了round函数、字符串格式化、decimal模块以及银行家舍入等方法,并强调了使用Decimal模块进行精确控制的重要性。
摘要由CSDN通过智能技术生成

四舍五入」是小学就学过的数学知识,也是日常计算中经常会用到的处理方法。

然而让人没想到的是,一个简单的四舍五入操作,在Python里居然这么难搞,网上还一堆错误的教程。

来看这个例子,有一个变量a为1.135,现在希望把它保留2位小数,要怎么做?

网上搜索一下,找到两种方法:

第1种,round函数。第一个参数是原数字,第二个参数是要保留的小数位数

round(a, 2)

结果 1.14,没有问题。

第2种,通过格式说明符.f对浮点数进行字符串格式化,f前加上要保留的小数位数。

这种写法在 %格式化、format方法和f-string 上均适用。

print('%.2f' % a, '{:.2f}'.format(a), f'{a:.2f}')

结果 1.14,也没有问题。

所以看来,以上两种方法都可以实现四舍五入地保留小数位数……

图片

but,真的是这样吗?

显然事情没这么简单。如果把a的值改成1.125,再跑一下之前的代码,就发现两种方法都不对了。

图片

别小看这么一点误差,我曾经在做助教时,就因为类似的原因,导致一位原本算好被60分放过的同学挂了科。

图片

有些不靠谱的半瓶水教程会跟你说,这是因为Python用了种叫做「四舍六入五成双」的保留机制:5前面的数字是奇数就进位,是偶数就保持不变,所以1.135会得到1.14,而1.125就是1.12。

图片

还有的教程告诉你有种方法可以实现四舍五入:就是把要保留N位的小数,乘以10的N次方,加上0.5后取整,再除以10的N次方

int(a * 10 ** 2 + 0.5) / 10 ** 2)

结果确实是 1.13。

但你以为这样就可以了吗?

如果再把a改成1.035。结果既没有什么所谓的奇数进位,也没能通过先乘再除的方法实现四舍五入。

图片

把1.005,1.015,1.025,一直到1.995,用前面提到过的3种方法保留2位小数的结果输出出来就会发现。round和字符串格式化得到的保留结果是一样的,且基本没有规律可言。

而先乘后除法虽然在大部分情况下是符合四舍五入的,但仍然有一些例外的情况。

图片

图片

在这里插入图片描述

导致这种现象的原因,和之前讲解过的 0.1 + 0.2 != 0.3 一样,都是因为浮点数的精度造成的。

让这些小数输出更多位数,就会看到,很多值虽然结尾是5,但在计算机中以二进制存储的实际值其实不到5。那么按照四舍五入来说,当然是要被舍去了。

在这里插入图片描述

真正可以做到对小数保留位数进行精确控制的方法是使用 Python 内置的 decimal 模块,它用于高精度的十进制算术运算。

用 round 函数对于 Decimal 类型对象进行保留,才是真正的四舍六入五成双。

from decimal import Decimal
x = 1.035
print(round(Decimal(str(x)), 2))

在这里插入图片描述

这种机制又被称作「银行家舍入」,它其实比四舍五入更合理。因为5是两个数的中间值,全都进位会让数据在整体分布上偏大,而银行家舍入规则可以让累积误差趋向于0。

如果你就是想要按照四舍五入来保留,也可以,通过将 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 对象,否则实际值仍然是带有误差的,从而导致四舍五入失效。

在这里插入图片描述

好吧,没想到一个简单的四舍五入操作,竟然还这么复杂,你学会了吗?

关于Python学习指南

学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

包括:Python激活码+安装包、Python web开发,Python爬虫,Python数据分析,人工智能、自动化办公等学习教程。带你从零基础系统性的学好Python!

👉Python所有方向的学习路线👈

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。(全套教程文末领取)

在这里插入图片描述

👉Python学习视频600合集👈

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

在这里插入图片描述

温馨提示:篇幅有限,已打包文件夹,获取方式在:文末

👉Python70个实战练手案例&源码👈

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

👉Python大厂面试资料👈

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

在这里插入图片描述

在这里插入图片描述

👉Python副业兼职路线&方法👈

学好 Python 不论是就业还是做副业赚钱都不错,但要学会兼职接单还是要有一个学习规划。

在这里插入图片描述

👉 这份完整版的Python全套学习资料已经上传,朋友们如果需要可以扫描下方CSDN官方认证二维码或者点击链接免费领取保证100%免费

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值