文章目录

  • 1. 0.1加0.2不等于0.3?
  • 2. 为什么要使用浮点数?
  • 3. 浮点数的二进制和十进制是怎样转换的?
  • 4. 如何实现0.1加0.2等于0.3?

1. 0.1加0.2不等于0.3?

什么?0.1 加 0.2 不等于 0.3?你确定没有搞错?真的,这是千真万确的事实。不仅 Python 如此,所有浮点数规范遵从IEEEE754二进制浮点数算术标准(ANSI/IEEE Std 754-1985)的编程语言,比如 C,同样如此(如果想在C环境中验证的话,请使用 double 类型)。我们在 Python 的 IDLE 中验证一下:

>>> 0.1 + 0.2 == 0.3
False
>>> 0.1 + 0.2
0.30000000000000004

为什么会这样呢? 0.1 + 0.2 和 0.3 究竟有什么差别?让我们用 Python 的方式来探究一番吧。我们知道,Python 的世界里,万物皆对象,浮点数也是如此,浮点数对象的方法里面究竟隐藏着怎样的秘密呢?

>>> a = 0.1 + 0.2
>>> b = 0.3
>>> a.hex()
'0x1.3333333333334p-2'
>>> b.hex()
'0x1.3333333333333p-2'

我们用对象 a 表示 0.1 + 0.2,用对象 b 表示 0.3, 浮点数对象的 hex() 方法,返回的是浮点数的十六进制表示。p-2,意思是为小数点左移2位,如果p+3,则表示右移3位。上面的代码,清晰显示了 a 和 b 的十六进制表示确实不一样。将 a 和 b 的 hex() 翻译成二进制是这样的:

a.hex(): 0.01 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0100
b.hex(): 0.01 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011

2. 为什么要使用浮点数?

天地茫茫,宇宙无限,星际距离动辄以万亿公里计。然若专注于微观世界,人类认知已经进入了纳米范围之内(氢原子基态的电子轨道半径0.0528纳米)。如果使用定点数表示如此巨大的动态范围,势必耗费大量的存储资源,因此,浮点数就应运而生了。所谓浮点数,简单理解,就是科学计数法。比如:

太阳与地球之间的平均距离:150000000000米,记作 1.5 × 1 0 11 1.5\times10^{11} 1.5×1011 米
氢原子基态的电子轨道半径:0.000000000528米,记作 5.28 × 1 0 − 11 5.28\times10^{-11} 5.28×10−11 米

Python 的浮点数为双精度浮点数,遵从 IEEEE754 标准,使用8个字节共64位表示一个浮点数,其中:

  • 符号位:1位
  • 指数位:11位
  • 尾数位:52位

我们来看看双精度浮点数的有关信息。

>>> import sys
>>> sys.float_info
sys.float_info(
max=1.7976931348623157e+308,
max_exp=1024,
max_10_exp=308,
min=2.2250738585072014e-308,
min_exp=-1021,
min_10_exp=-307,
dig=15,
mant_dig=53,
epsilon=2.220446049250313e-16,
radix=2,
rounds=1)

Python 的双精度浮点数,可以表示的值域范围:

最小值: 2.2250738585072014 × 1 0 − 308 2.2250738585072014\times10^{-308} 2.2250738585072014×10−308
最大值: 1.7976931348623157 × 1 0 308 1.7976931348623157\times10^{308} 1.7976931348623157×10308

1位符号位表示浮点数的正负,很容易理解;11位指数,对应的底数是2,可以表示的指数的整数范围应该是-1024~1023,为什么 sys.float_info 显示的 min_exp 和 max_exp 却分别是 -1021 和 1024 呢?为什么尾数是看起来这么奇怪呢?

3. 浮点数的二进制和十进制是怎样转换的?

我们知道,二进制整数从低位到高位,权重分别是1、2、4、8等;二进制小数也类似,从小数点后第1位向右,权重分别是0.5、0.25、0.125、0.0625等。据此,可以很容易地将浮点数的二进制转换成十进制。比如,0b0.1,代表十进制的0.5,0b0.01,代表十进制的0.25,0b0.11,代表十进制的0.75。
我们以十进制浮点数 9.25 为例,看看浮点数对象是如何存储的。9.25 写成二进制是:

0b1001.01

小数点左移3位,整数位只保留1位(类似于十进制的科学记数法):

0b1.00101

现在的尾数是00101,转成十六进制(需要后补3个0变成8位)是0x28。因为小数点左移3位,指数是3。我们来验证一下:

>>> a = 9.25
>>> a.hex()
'0x1.2800000000000p+3'

结果和我们手工转换完全一致。双精度浮点数的尾数,因为默认整数位有一个1,所以双精度浮点数所能表示的最大数的指数,也就从1023变成了1024。当52位尾数全部位1,指数位1024时,代表的最大数是 2 1024 2^{1024} 21024,变成以10为底的数,尾数应当是:

>>> import math
>>> math.log10(math.pow(2,1000)) + math.log10(math.pow(2,24)) # log10(2^1024)
308.25471555991675
>>> _ - 308
0.25471555991674677
>>> math.pow(10, _)
1.7976931348623277

这与 sys.float_info 显示的尾数 1.7976931348623157 几乎完全一致。双精度浮点数所能表示的最小数的指数和尾数,和刚才的推导基本相似,我们就不再赘述了。

下面给出浮点数的二进制和十进制互转函数:

# -*- coding: utf-8 -*-import mathdef decimal_bin(number, precision=52):"""十进制浮点数转二进制字符串,precision为二进制小数位位数"""num_int = int(number)num_dec = number - num_ints = ''while num_dec > 0 and len(s)<precision:num_dec *= 2if num_dec >= 1:s += '1'num_dec -= 1else:s += '0'return '%s.%s'%(bin(num_int), s)def bin_decimal(bin_str):"""二进制字符串转十进制浮点数"""num_int, num_dec = bin_str.split('.')s = int(num_int, base=2)for i, bit in enumerate(num_dec):if bit == '1':s += math.pow(2, -(i+1))return s

4. 如何实现0.1加0.2等于0.3?

通过刚才的分析,我们已经认识到了浮点数的本质:在有限的精度内,我们可以精确的设置浮点数对象的值,但一旦参与运算,因为精度误差的原因,将会导致计算结果和预期的数学结果不能完全一致。那么,在浮点运算时,我们有没有方法实现计算结果和预期的数学结果保持一致呢?答案是:有!万能的 Python 已经为我们准备了模块 decimal,专门解决这个问题。请看演示:

>>> from decimal import Decimal
>>> a = 0.1
>>> b = 0.2
>>> c = float(Decimal(str(a)) + Decimal(str(b)))
>>> c
0.3

decimal 模块还有很多功能,有兴趣的同学可以在网上找到更多的学习资料。

从0.1加0.2不等于0.3谈Python浮点数的前世今生相关推荐

  1. android 8.0 一加5,一加5如何升级安卓8.0 一加5升级安卓8.0图文教程

    2017-12-26 17:18:05 一加5如何升级安卓8.0 一加5升级安卓8.0图文教程 标签:一加5,一加5升级,一加5 8.0刷机包 一加5如何升级安卓8.0,rom基地小编今天带来一加5升 ...

  2. android5.0刷机,一加手机怎么升级安卓5.0 一加手机刷Android 5.0教程

    谷歌今天正式发布了最新的安卓5.0正式版,作为一次非常大的系统升级,安卓5.0带来了非常多明显的升级,体验上相比安卓4.4要精进不少.在众多手机纷纷向Android 5.0发起冲击之时,日一加手机官方 ...

  3. 一加5t升级android8.0,一加5/5T升级Android8.0 这些强大的功能用起来太爽了!

    原标题:一加5/5T升级Android8.0 这些强大的功能用起来太爽了! 众所周知,一加在系统版本更新方面非常积极,更新Android版本不仅能够增加系统的功能,在安全性,稳定性,流畅性等各方面都会 ...

  4. 判断一个doule等于0的正确方法

    doule进行数学运算时会出现精度问题,判断double是否等于0是不能用"d==0" 要用下面的方法: public static void main(String[] args ...

  5. 【问题思考总结】拉格朗日法的条件极值中的λ可以等于0吗(三种方法)

    问题 在做这道题的时候,我在对变量消元的时候,直接放弃了λ=0的情况,原因很简单,这个λ=0不是就是无条件极值了嘛,怎么可能呢?然而经过查阅资料和思考发现,并不是这样.于是在前人的基础上通过比较无条件 ...

  6. python计算1的平方减2的平方加3的平方减4的平方怎么算_已知X的平方加4x减一等于零 求2x的四次方加八X的三次方减四X的平方减八X加一的值...

    已知X的平方加4x减一等于零 求2x的四次方加八X的三次方减四X的平方减八X加一的值以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来 ...

  7. 算法-----三数之和等于0

    三数之和 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件 且不重复的三元组. 注意:答案中不可以包含重 ...

  8. python0.1+0.2不等于0.3_为什么0.1 + 0.2不等于0.3?

    原标题:为什么0.1 + 0.2不等于0.3? 0.1 + 0.2不等于0.3这是一个普遍的问题,例如在JS控制台输入将得到0.30000000000000004 在python的控制台也是输出这个数 ...

  9. 如何解决JavaScript中0.1+0.2不等于0.3

    原文转载自:https://www.cnblogs.com/weshare/archive/2018/02/20/8455470.html >console.log(0.1+0.2===0.3) ...

最新文章

  1. php面试常问的问题
  2. Active Diretory 全攻略(三)--建立域(2)
  3. 【每周NLP论文推荐】 聊天机器人中FAQ相关的论文推荐
  4. C# MVC 用户登录状态判断
  5. MRC522(2):超简易门禁
  6. 记一次easywechat企业付款问题
  7. MySQL 8 新特性之持久化全局变量的修改
  8. 【GNN框架系列】DGL第二讲:使用Deep Graph Library实现GNN进行链接预测
  9. wpsppt放映时间_wps ppt如何制作时间倒计时
  10. 一、Geos库的安装和计算多边形是否相交
  11. java 拼音_JAVA实现汉字转换为拼音 pinyin4j/JPinyin
  12. C Primer Plus(第6版)第十章复习题答案
  13. 大学计算机基础知识学习计划,大学个人学习计划
  14. oppo便签误删怎么办_OPPO手机便签删除了怎么恢复?有无需登录云端就可以恢复的备忘录软件吗...
  15. Java面试宝典之:基础篇
  16. 无法登录苹果开发者_如何申请苹果开发者帐号?有哪些注意事项?
  17. JSP WAP 开发
  18. RPA for Python(tagui)避坑指南 - 以咸鱼之王为例
  19. simple rpc framework
  20. ivm 无法播放 解决

热门文章

  1. [分析]欢乐时光源码
  2. 信号能量密度公式_信号理论(总结)..ppt
  3. 百亿节点、毫秒级延迟,携程金融基于 NebulaGraph 的大规模图应用实践
  4. 根据用户输入的半径绘制圆形
  5. 基于Django3.0的Python版网易云音乐API
  6. html5入门 epub,分享《HTML5与CSS3基础教程(第7版) 》(图灵程序设计丛书) pdf epub azw3格式...
  7. SQL SERVER添加表注释、字段注释
  8. github 加速(基于gitee)
  9. python内置函数系列之set(一)(持续更新)
  10. TTL与RS-485电平转换芯片MAX485/MAX3485