资讯专栏INFORMATION COLUMN

深度剖析0.1 +0.2===0.30000000000000004的原因

haobowd / 444人阅读

摘要:吐槽一句,大二的专业课数字逻辑电路终于用在工作上了。,整数位为,且精度只到十分位,因此是。如果是不限精度的话,转换后的二进制数应该是无限循环。再看一下百科给出的标准因此,的类型,最高的位是符号位,接着的位是指数,剩下的位为有效数字。

用一句话概括就是:
EcmaScrpt规范定义Number的类型遵循了IEEE754-2008中的64位浮点数规则定义的小数后的有效位数至多为52位导致计算出现精度丢失问题!

如果你看不懂这句话,仔细阅读本篇博客就对了!

首先看下10进制转换为2进制的方法。

数字逻辑电路上的算法是 (0.1)10 = (0.0)2。

吐槽一句,大二的专业课数字逻辑电路终于用在工作上了。

0.1*2 = 0.2 ,整数位为0,且精度只到十分位,因此是0.0。

如果是不限精度的话,转换后的二进制数应该是:0.000110011001100110011(0011)无限循环。

如果表示成一个奇怪的形式,则是 (-1)^0*1.100110011(0011)* 2^-4

上述式子可类比十进制科学计数法公式。

0.0001234567 = 1.234567 * 10^-4 

为什么要这样表示?

-1的0次幂又是什么意思?

这是国际标准组织IEEE754对于浮点数表示方式的一种定义。

格式为;

(-1)^S x Mx 2^E

各符号的意思如下:
S,是符号位,决定正负,0时为正数,1时为负数。
M,是指有效位数,大于1小于2。 
E,是指数位。

因此才有了下面的形式:

(-1)^0*1.100110011(无限循环0011) * 2^-4
S = 0,M = 1.100110011(无限循环0011),E =-4

对应的0.2为:

(-1)^0*1.100110011(无限循环0011) * 2^-3
S = 0 ,M = 1.100110011(无限循环0011),E =-3

那么这和javascript有什么关系呢?

因为IEEE754标准里,还有两种特殊的定义。

IEEE 754规定,对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。

对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

问题还是一样,这和我们的javascript有什么关系呢?

因为javascript中Number类型,就是严格按照IEEE754标准来定义的。下面给出了最新版的ecma-262版本中关于Number类型的定义。

*6.1.6
The Number Type**
The Number type has exactly 18437736874454810627 (that is, ) values, representing the double-precision 64-bit format IEEE 754-2008 values as specified in the IEEE Standard for Binary Floating-Point Arithmetic, except that the 9007199254740990 (that is, ) distinct “Not-a-Number” values of the IEEE Standard are represented in ECMAScript as a single special  value.

再看一下wiki百科给出的IEEE754标准:

因此,javascript的Number类型, 最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

拿0.1举例来说:

(-1)^01.100110011(无限循环0011) 2^-4

= 0,M = 1.100110011(无限循环0011),E =-4

这里的无限循环就有限了,循环位数最多只能有52位.

JS中的0.1,在引擎中运算时,实质上会编译成:

1.1001100110011001100110011001100110011001100110011001*2^-4

0.2同理,会编译成:

1.1001100110011001100110011001100110011001100110011001*2^- 3

拿出关键的指数部分和有效位部分:

-4  0.1001100110011001100110011001100110011001100110011001 ①

-3  0.1001100110011001100110011001100110011001100110011001 ②·

①式转化为纯小数,小数最低位的1001被高位的0000挤出有效范围,得到③式

②式转化为纯小数,小数最低位的001被高位的000挤出有效范围,得到④式

原因是什么?

原因就是JS中的Number类型,二进制小数的有效位数只有52位,从0到51位(包括边界)。

在chrome控制台输入(0.1).toString("2")并打印结果为:"0.0001100110011001100110011001100110011001100110011001101"

不多不少,小数部分刚好52位,与规范以及我们的猜想完全契合。

回到0.1+0.2===0.30000000000000004这个经典问题。

在EcmaScript中,无关Browser环境,还是Nodejs环境,0.1+0.2的实际计算过程如下:

     0.0000100110011001100110011001100110011001100110011001 ③

    +0.0001001100110011001100110011001100110011001100110011 ④

---------------------------------------------------------------------------------------------------

    =0.0100110011001100110011001100110011001100110011001100 ⑤

最后得到的⑤式其实0.300000000000000004(17位十进制数)的二进制形式。

这就是0.1+0.2 ===0.300000000000000004的原因。

虽然我们期望的理想结果是返回0.3,恰恰印证了现实往往很骨感的说法。

有没有让0.1+02返回为0.3的办法呢?

因为不只是这一个精度丢失特例,还有很多情况都会造成精度丢失,比如:

0.3 / 0.1===2.9999999999999996以及0.7 * 180==125.99999999998等等。

那么有没有办法解决这个问题呢?

移步下一篇博客:如何解决0.1 +0.2===0.30000000000000004类问题

鸣谢单位:

https://segmentfault.com/a/1190000005022170

http://demon.tw/copy-paste/javascript-precision.html

http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html

http://www.css88.com/archives/7340#more-7340

http://www.ecma-international.org/ecma-262/8.0/index.html

https://en.wikipedia.org/wiki/Floating-point_arithmetic#Internal_representation

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89591.html

相关文章

  • 如何解决0.1 +0.2===0.30000000000000004类问题

    摘要:方法使用定点表示法来格式化一个数,会对结果进行四舍五入。该数值在必要时进行四舍五入,另外在必要时会用来填充小数部分,以便小数部分有指定的位数。如果数值大于,该方法会简单调用并返回一个指数记数法格式的字符串。在环境中,只能是之间,测试版本为。 showImg(https://segmentfault.com/img/remote/1460000011913134?w=768&h=521)...

    yuanzhanghu 评论0 收藏0
  • JavasScript重难点知识

    摘要:忍者级别的函数操作对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是创建一个供以后使用的函数。 JS 中的递归 递归, 递归基础, 斐波那契数列, 使用递归方式深拷贝, 自定义事件添加 这一次,彻底弄懂 JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果...

    forsigner 评论0 收藏0
  • 利用babel(AST)优雅地解决0.1+0.2!=0.3问题

    摘要:因此利用以及语法树在代码构建过程中重写等符号,开发时直接以这样的形式编写代码,在构建过程中编译成,从而在开发人员无感知的情况下解决计算失精的问题,提升代码的可读性。 前言 你了解过0.1+0.2到底等于多少吗?那0.1+0.7,0.8-0.2呢? 类似于这种问题现在已经有了很多的解决方案,无论引入外部库或者是自己定义计算函数最终的目的都是利用函数去代替计算。例如一个涨跌幅百分比的一个...

    张巨伟 评论0 收藏0
  • TensorFlow入门教程

    摘要:简介是目前最流行的深度学习框架。代表一个数学运算,简称,这里面包括了深度学习模型经常需要使用的。这也是名字的由来,表示多维数组在中流动。这一步指定求解器,并设定求解器的最小化目标为损失。 简介 TensorFlow是目前最流行的深度学习框架。我们先引用一段官网对于TensorFlow的介绍,来看一下Google对于它这个产品的定位。 TensorFlow™ is an open sou...

    XUI 评论0 收藏0

发表评论

0条评论

haobowd

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<