Python浮点数四舍五入问题的分析与解决方法


Posted in Python onNovember 19, 2019

问题

昨天遇到一个问题,在 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.115,它的小数点后第三位实际上是 4,当然会被舍去。
如果你的这个小数在计算机中能被精确表示,那么,round 采用的进位机制是奇进偶舍,所以这取决于你要保留的那一位,它是奇数还是偶数,以及它的下一位后面还有没有数据。

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

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

> (exact 6.6045)
3718002967371055/562949953421312

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

如何正确进行四舍五入

如果要实现数学上的四舍五入,那么就需要使用 decimal 模块,具体用法参考官方文档:https://docs.python.org/zh-cn...。
其中 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…

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Windows平台Python连接sqlite3数据库的方法分析
Jul 12 Python
Python tkinter模块中类继承的三种方式分析
Aug 08 Python
Python遍历numpy数组的实例
Apr 04 Python
selenium设置proxy、headers的方法(phantomjs、Chrome、Firefox)
Nov 29 Python
详解Python正则表达式re模块
Mar 19 Python
python2.7使用plotly绘制本地散点图和折线图
Apr 02 Python
Python2.7版os.path.isdir中文路径返回false的解决方法
Jun 21 Python
如何用Python破解wifi密码过程详解
Jul 12 Python
python使用Pandas库提升项目的运行速度过程详解
Jul 12 Python
python统计指定目录内文件的代码行数
Sep 19 Python
使用Python第三方库pygame写个贪吃蛇小游戏
Mar 06 Python
一些让Python代码简洁的实用技巧总结
Aug 23 Python
python 上下文管理器及自定义原理解析
Nov 19 #Python
浅析python中while循环和for循环
Nov 19 #Python
django实现web接口 python3模拟Post请求方式
Nov 19 #Python
wxPython+Matplotlib绘制折线图表
Nov 19 #Python
python元组的概念知识点
Nov 19 #Python
python数值基础知识浅析
Nov 19 #Python
基于python实现蓝牙通信代码实例
Nov 19 #Python
You might like
CI框架在CLI下执行占用内存过大问题的解决方法
2014/06/17 PHP
WordPress中查询文章的循环Loop结构及用法分析
2015/12/17 PHP
获取当前网页document.url location.href区别总结
2008/05/10 Javascript
Js日期选择器并自动加入到输入框中示例代码
2013/08/02 Javascript
jQuery 处理页面的事件详解
2015/01/20 Javascript
Node.js+Express配置入门教程详解
2016/05/19 Javascript
JQuery EasyUI学习教程之datagrid 添加、修改、删除操作
2016/07/09 Javascript
JS从数组中随机取出几个数组元素的方法
2016/08/02 Javascript
VUE2.0+ElementUI2.0表格el-table实现表头扩展el-tooltip
2018/11/30 Javascript
微信小程序第三方框架对比 之 wepy / mpvue / taro
2019/04/10 Javascript
javascript设计模式 ? 观察者模式原理与用法实例分析
2020/04/22 Javascript
[39:11]DOTA2上海特级锦标赛C组资格赛#2 LGD VS Newbee第二局
2016/02/28 DOTA
[32:39]完美世界DOTA2联赛循环赛 Forest vs Inki BO2第一场 11.04
2020/11/04 DOTA
在Windows服务器下用Apache和mod_wsgi配置Python应用的教程
2015/05/06 Python
Python实现在线音乐播放器
2017/03/03 Python
详解Django中间件执行顺序
2018/07/16 Python
Python for循环通过序列索引迭代过程解析
2020/02/07 Python
详细分析Python可变对象和不可变对象
2020/07/09 Python
利用HTML5中的Canvas绘制一张笑脸的教程
2015/05/07 HTML / CSS
HTML+CSS+JavaScript实现图片3D展览的示例代码
2020/10/12 HTML / CSS
俄罗斯和世界各地的酒店预订:Hotels.com俄罗斯
2016/08/19 全球购物
Black Halo官方网站:购买连衣裙、礼服和连体裤
2018/06/13 全球购物
do you have any Best Practice for testing
2016/06/04 面试题
教师自我评价范例
2013/09/24 职场文书
少先队学雷锋活动月总结
2014/03/09 职场文书
李培根演讲稿
2014/05/22 职场文书
大型演出策划方案
2014/05/28 职场文书
科技节口号
2014/06/19 职场文书
感情真挚的毕业生求职信
2014/07/19 职场文书
班级活动总结格式
2014/08/30 职场文书
关于教师节的演讲稿
2014/09/04 职场文书
通知范文怎么写
2015/04/16 职场文书
单位政审意见范文
2015/06/04 职场文书
深度好文:50条没人告诉你的人生经验,句句精辟
2019/08/22 职场文书
Ajax实现异步加载数据
2021/11/17 Javascript
日本动漫十大公认神作:第五现已全网禁播,《死亡笔记》在榜
2022/03/18 日漫