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中对list去重的多种方法
Sep 18 Python
python中字符串的操作方法大全
Jun 03 Python
python实现时间o(1)的最小栈的实例代码
Jul 23 Python
python开启摄像头以及深度学习实现目标检测方法
Aug 03 Python
python变量赋值方法(可变与不可变)
Jan 12 Python
python3+selenium实现126邮箱登陆并发送邮件功能
Jan 23 Python
Python 通过requests实现腾讯新闻抓取爬虫的方法
Feb 22 Python
django与vue的完美结合_实现前后端的分离开发之后在整合的方法
Aug 12 Python
Python 函数用法简单示例【定义、参数、返回值、函数嵌套】
Sep 20 Python
Python爬取爱奇艺电影信息代码实例
Nov 26 Python
python GUI库图形界面开发之PyQt5日期时间控件QDateTimeEdit详细使用方法与实例
Feb 27 Python
基于Python pyecharts实现多种图例代码解析
Aug 10 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
如何实现给定日期的若干天以后的日期
2006/10/09 PHP
深入PHP购物车模块功能分析(函数讲解,附源码)
2013/06/25 PHP
php实现不通过扩展名准确判断文件类型的方法【finfo_file方法与二进制流】
2017/04/18 PHP
Linux下 php7安装redis的方法
2018/11/01 PHP
Yii2框架配置文件(Application属性)与调试技巧实例分析
2019/05/27 PHP
PHP实现获取url地址中顶级域名的方法示例
2019/06/05 PHP
Javascript保存网页为图片借助于html2canvas库实现
2014/09/05 Javascript
JavaScript通过字符串调用函数的实现方法
2015/03/18 Javascript
Underscore.js 1.3.3 中文注释翻译说明
2015/06/25 Javascript
js转换对象为xml
2017/02/17 Javascript
移动端利用H5实现压缩图片上传功能
2017/03/29 Javascript
vue-router 组件复用问题详解
2018/01/22 Javascript
javascript使用正则实现去掉字符串前面的所有0
2018/07/23 Javascript
从vue源码看props的用法
2019/01/09 Javascript
详解webpack编译速度提升之DllPlugin
2019/02/05 Javascript
Python 实现文件的全备份和差异备份详解
2016/12/27 Python
Python线程同步的实现代码
2018/10/03 Python
Python骚操作之动态定义函数
2019/03/26 Python
Python 硬币兑换问题
2019/07/29 Python
python爬虫 基于requests模块的get请求实现详解
2019/08/20 Python
Python线程条件变量Condition原理解析
2020/01/20 Python
动态设置django的model field的默认值操作步骤
2020/03/30 Python
Python新手学习raise用法
2020/06/03 Python
Python加载数据的5种不同方式(收藏)
2020/11/13 Python
python中pyqtgraph知识点总结
2021/01/26 Python
CSS3教程(6):创建网站多列
2009/04/02 HTML / CSS
美国网上订购鲜花:FTD
2016/09/23 全球购物
DOM和JQuery对象有什么区别
2016/11/11 面试题
施工资料员的岗位职责
2013/12/22 职场文书
2015届大学生就业推荐表自我评价
2014/09/27 职场文书
工作作风整顿个人剖析材料
2014/10/11 职场文书
自我检讨书范文
2015/01/28 职场文书
《刺客之王:C罗全景传记》:时代从来不会亏待手艺人
2019/11/28 职场文书
pdf论文中python画的图Type 3 fonts字体不兼容的解决方案
2021/04/24 Python
pandas中DataFrame重置索引的几种方法
2021/05/24 Python
Python的三个重要函数详解
2022/01/18 Python