Python中关于浮点数的冷知识


Posted in Python onSeptember 22, 2019

本周的PyCoder's Weekly 上分享了一篇小文章,它里面提到的冷知识很有意思,我稍作补充,分享给大家。

它提到的部分问题,读者们可以先思考下:

  • 若两个元组相等,即 a==b 且 a is b,那么相同索引的元素(如 a[0] 、b[0])是否必然相等呢?
  • 若两个对象的 hash 结果相等,即 hash(a) == hash(b),那么它们是否必然相等呢?

答案当然都为否(不然就不叫冷知识了),大家可以先尝试回答一下,然后再往下看。

-----思考分割线-----

好了,先来看看第一个问题。两个相同的元组 a、b,它们有如下的关系:

>>> a = (float('nan'),)
>>> b = a
>>> a  # (nan,)
>>> b  # (nan,)

>>> type(a), type(b)
(<type 'tuple'>, <type 'tuple'>)

>>> a == b
True

>>> a is b # 即 id(a) == id(b)
True

>>> a[0] == b[0]
False

以上代码表明:a 等于 b(类型、值与 id 都相等),但是它们的对位元素却不相等。

两个元组都只有一个元素(逗号后面没有别的元素,这是单元素的元组的表示方法,即 len(a)==1 )。float() 是个内置函数,可以将入参构造成一个浮点数。

为什么会这样呢?先查阅一下文档,这个内置函数的解析规则是:

sign      ::= "+" | "-"
infinity    ::= "Infinity" | "inf"
nan      ::= "nan"
numeric_value ::= floatnumber | infinity | nan
numeric_string ::= [sign] numeric_value

它在解析时,可以解析前后的空格、前缀的加减号(+/-)、浮点数,除此之外,还可以解析两类字符串(不区分大小写):"Infinity"或"inf",表示无穷大数;“nan”,表示不是数(not-a-number),确切地说,指的是除了数以外的所有东西。

前面分享的第一个冷知识就跟“nan”有关,作为整体,两个元组相等,但是它们唯一的元素却不相等。之所以会这样,因为“nan”表示除了数以外的东西,它是一个范围,所以不可比较。

作为对比,我们来看看两个“无穷大的浮点数”是什么结果:

>>> a = (float('inf'),)
>>> b = a
>>> a  # (inf,)
>>> b  # (inf,)

>>> a == b # True
>>> a is b # True
>>> a[0] == b[0] # True

注意最后一次比较,它跟前面的两个元组恰好相反,由此,我们可以得出结论:两个无穷大的浮点数,数值相等,而两个“不是数的东西”,数值不相等。

化简一下,可以这样看:

>>> a = float('inf')
>>> b = float('inf')
>>> c = float('nan')
>>> d = float('nan')

>>> a == b # True
>>> c == d # False

以上就是第一个冷知识的揭秘。接着看第二个:

>>> hash(float('nan')) == hash(float('nan'))
True

前面刚说了两个“不是数的东西”不相等,这里却显示它们的哈希结果相等,这挺违背常理的。

我们可以推理出一条简单的结论:不相等的两个对象,其哈希结果可能相等。

原因在于,hash(float('nan')) 的结果等于 0,它是个固定值,作比较时当然就相等了。

其实,关于 hash() 函数,还埋了一个彩蛋:

>>> hash(float('inf')) # 314159
>>> hash(float('-inf')) # -314159

有没有觉得这个数值很熟悉啊?它正是圆周率的前五位 3.14159,去除小数点后的结果。在早期的 Python 版本中,负无穷大数的哈希结果其实是 -271828,正是取自于自然对数 e。这两个数都是硬编码在 Python 解释器中的,算是某种致敬吧。

由于 float('nan') 的哈希值相等,这通常意味着它们不可以作为字典的不同键值,但是事实却出人意料:

>>> a = {float('nan'): 1, float('nan'): 2}
>>> a
{nan: 1, nan: 2}

# 作为对比:
>>> b = {float('inf'): 1, float('inf'): 2}
>>> b
{inf: 2}

如上所示,两个 nan 键值在表示上一模一样(注意,它们没有用引号括起来),它们可以共存,而 inf 却只能归并成一个,再次展示出了 nan 的神奇。

好了,两个很冷的小知识分享完毕,背后的原因都在于 float() 取浮点数时,Python 允许了 nan(不是数)的存在,它表示不确切的存在,所以导致了这些奇怪的结果。

最后,我们作下小结:

  • 包含 float('nan') 的两个元组,当做整体作比较时,结果相等;两个相等的元组,其对位的元素可能不相等
  • float('nan') 表示一个“不是数”的东西,它本身不是确定值,两个对象作比较时不相等,但是其哈希结果是固定值,作比较时相等;可用作字典的键值,而且是不冲突的键值
  • float('inf') 表示一个无穷大的浮点数,可看作确定的值,两个对象做比较时相等,其哈希结果也相等;可用作字典的键值,但是会产生冲突
  • float('nan') 的哈希结果为 0,float('inf') 的哈希结果为 314159

参考资料:

https://docs.python.org/3/library/functions.html#float

https://www.pythondoeswhat.com/2019/09/welcome-to-float-zone.html

总结

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

Python 相关文章推荐
用Python实现web端用户登录和注册功能的教程
Apr 30 Python
详解python中executemany和序列的使用方法
Aug 12 Python
Python解决八皇后问题示例
Apr 22 Python
tensorflow saver 保存和恢复指定 tensor的实例讲解
Jul 26 Python
解决python flask中config配置管理的问题
Jul 26 Python
3行Python代码实现图像照片抠图和换底色的方法
Oct 10 Python
python中with用法讲解
Feb 07 Python
python:HDF和CSV存储优劣对比分析
Jun 08 Python
面向新手解析python Beautiful Soup基本用法
Jul 11 Python
浅谈Django前端后端值传递问题
Jul 15 Python
Python3合并两个有序数组代码实例
Aug 11 Python
使用Python快速打开一个百万行级别的超大Excel文件的方法
Mar 02 Python
Python安装及Pycharm安装使用教程图解
Sep 20 #Python
Python实现语音识别和语音合成功能
Sep 20 #Python
使用python将最新的测试报告以附件的形式发到指定邮箱
Sep 20 #Python
Python使用__new__()方法为对象分配内存及返回对象的引用示例
Sep 20 #Python
Python 类方法和实例方法(@classmethod),静态方法(@staticmethod)原理与用法分析
Sep 20 #Python
Python 类属性与实例属性,类对象与实例对象用法分析
Sep 20 #Python
使用python脚本自动创建pip.ini配置文件代码实例
Sep 20 #Python
You might like
PHP PDOStatement:bindParam插入数据错误问题分析
2013/11/13 PHP
Yii2实现多域名跨域同步登录退出
2017/02/04 PHP
PHP设计模式之装饰器模式实例详解
2018/02/07 PHP
PHP面向对象程序设计(OOP)之方法重写(override)操作示例
2018/12/21 PHP
Windows平台PHP+IECapt实现网页批量截图并创建缩略图功能详解
2019/08/02 PHP
jquery插件制作 表单验证实现代码
2012/08/17 Javascript
javascript中数组的定义及使用实例
2015/01/21 Javascript
js实现文本框中输入文字页面中div层同步获取文本框内容的方法
2015/03/03 Javascript
jQuery实现分隔条左右拖动功能
2015/11/21 Javascript
Nodejs进阶:如何将图片转成datauri嵌入到网页中去实例
2016/11/21 NodeJs
Javascript 实现计算器时间功能详解及实例(二)
2017/01/08 Javascript
Node.js中流(stream)的使用方法示例
2017/07/16 Javascript
Vue.js在数组中插入重复数据的实现代码
2017/11/17 Javascript
安装vue-cli的简易过程
2018/05/22 Javascript
node 版本切换的实现
2020/02/02 Javascript
JS绘图Flot应用图形绘制异常解决方案
2020/10/16 Javascript
[01:02:09]Liquid vs TNC 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.21
2020/07/19 DOTA
总结用Pdb库调试Python的方式及常用的命令
2016/08/18 Python
python爬虫开发之PyQuery模块详细使用方法与实例全解
2020/03/09 Python
Python selenium键盘鼠标事件实现过程详解
2020/07/28 Python
Python爬虫之Selenium中frame/iframe表单嵌套页面
2020/12/04 Python
详解Python中@staticmethod和@classmethod区别及使用示例代码
2020/12/14 Python
使用CSS3的::selection改变选中文本颜色的方法
2015/09/29 HTML / CSS
乌克兰网上服装店:Bolf.ua
2018/10/30 全球购物
如何在Cookie里面保存Unicode和国际化字符
2013/05/25 面试题
杭州信雅达系统.NET工程师面试试题
2015/02/08 面试题
财务副总经理工作职责
2013/11/25 职场文书
求职自荐信格式
2013/12/04 职场文书
学校庆元旦歌咏比赛主持词
2014/03/18 职场文书
大学毕业生个人自荐书
2014/07/02 职场文书
党员对照检查材料思想汇报(党的群众路线)
2014/09/24 职场文书
警察正风肃纪剖析材料
2014/10/16 职场文书
2014财务部年度工作总结
2014/12/08 职场文书
青年文明号申报材料
2014/12/23 职场文书
财务统计员岗位职责
2015/04/14 职场文书
2021-4-3课程——SQL Server查询【2】
2021/04/05 SQL Server