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 相关文章推荐
python使用PyGame模块播放声音的方法
May 20 Python
Python松散正则表达式用法分析
Apr 29 Python
Python 关于反射和类的特殊成员方法
Sep 14 Python
python多任务及返回值的处理方法
Jan 22 Python
在python中对变量判断是否为None的三种方法总结
Jan 23 Python
python中的协程深入理解
Jun 10 Python
python实现最大子序和(分治+动态规划)
Jul 05 Python
django将网络中的图片,保存成model中的ImageField的实例
Aug 07 Python
python禁用键鼠与提权代码实例
Aug 16 Python
Django实现CAS+OAuth2的方法示例
Oct 30 Python
通过实例解析python描述符原理作用
Jan 22 Python
Django-celery-beat动态添加周期性任务实现过程解析
Nov 26 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
PHP包含文件函数include、include_once、require、require_once区别总结
2014/04/05 PHP
PHP连接MYSQL数据库实例代码
2016/01/20 PHP
ThinkPHP 在阿里云上的nginx.config配置实例详解
2017/10/11 PHP
Laravel5.1 框架控制器基础用法实例分析
2020/01/04 PHP
PHP中关于php.ini参数优化详解
2020/02/28 PHP
强悍无比的WEB开发好助手FireBug(Firefox Plugin)
2007/01/16 Javascript
关于viewport,Ext.panel和Ext.form.panel的关系
2009/05/07 Javascript
javascript与cookie 的问题详解
2013/11/11 Javascript
我的Node.js学习之路(一)
2014/07/06 Javascript
浅谈 javascript 事件处理
2015/01/04 Javascript
jQuery实现时尚漂亮的弹出式对话框实例
2015/08/07 Javascript
animate 实现滑动切换效果【实例代码】
2016/05/05 Javascript
使用JS中的exec()方法构造正则表达式验证
2016/08/01 Javascript
微信小程序用户自定义模版用法实例分析
2017/11/28 Javascript
浅谈angularJs函数的使用方法(大小写转换,拷贝,扩充对象)
2018/10/08 Javascript
Vue 刷新当前路由的实现代码
2019/09/26 Javascript
python列表与元组详解实例
2013/11/01 Python
Python使用minidom读写xml的方法
2015/06/03 Python
利用Python实现命令行版的火车票查看器
2016/08/05 Python
django批量导入xml数据
2016/10/16 Python
Python使用MD5加密算法对字符串进行加密操作示例
2018/03/30 Python
浅谈Python批处理文件夹中的txt文件
2019/03/11 Python
Python利用matplotlib绘制约数个数统计图示例
2019/11/26 Python
pycharm导入源码的具体步骤
2020/08/04 Python
Python实现粒子群算法的示例
2021/02/14 Python
台湾饭店和机票预订网站:Expedia台湾
2016/08/05 全球购物
教你怎样写好自我评价
2013/10/05 职场文书
家具厂厂长岗位职责
2014/01/01 职场文书
便利店投资的创业计划书
2014/01/12 职场文书
给分销商的致歉信
2014/01/14 职场文书
英语教育专业自荐信
2014/05/29 职场文书
2015年公共机构节能宣传周活动总结
2015/03/26 职场文书
六一儿童节致辞
2015/07/31 职场文书
2016大一新生入学教育心得体会
2016/01/23 职场文书
mybatis 获取无数据的字段不显示的问题
2021/07/15 Java/Android
python读取并查看npz/npy文件数据以及数据显示方法
2022/04/14 Python