js中的0.1+0.2!==0.3

本文主要是意识流写法,想到哪写到哪, 估计有些废话, 自行过滤(逃...)。 前端开发应该都知道这是浮点数运算一个经典的问题了,网上关于这个问题的文章也一抓一大把,以前对这个问题也只是非常粗浅的知道这是浮点数的精度问题,具体原因并没有深究过,偶然又碰到一个与这个问题相关的问题,就再捋一下,深层次理解下。

遇到的问题和不理解的地方

  1. 0.1+0.2 !=0.3
  2. 安全整数范围MDN上是(-(2^53 - 1) ~ (2^53 - 1)),犀牛书上是 ( -2^53 ~ 2^53 )
  3. js的最大正数为什么是1.7976931348623157e+308
  4. js的最小正数为什么是5*10^-324

原因是什么

以上几个问题其实是JS采用的是IEEE754浮点数标准存储, 其他用这种标准的语言都是会有这样的问题的,因此理解这个标准,上面的就都能深层次的理解了。

js中的IEEE754浮点数标准

Js中number类型的数字都是采用64位的双精度浮点数进行存储的,其中1个符号位(0正1负),11个指数位,52个尾数位。

每一个数字转换成二进制的科学计数法,然后得到符号位、指数位和尾数进行存储。与十进制的科学计数法类似,比如10进制中12345678=1.2345678*10^7 ,符号位(对应二进制中的符号位)是正, 小数2345678(对应二进制中的尾数),指数7(对应二进制中的指数)。值得注意的是二进制科学计数法表示中的指数位11位能取到的值是0~2047,我们知道十进制中可以有负数表示小数,同样二进制中也应该有负数, 采取的方法就是取2013的偏移值,真正的指数表示范围就是-2013~2014。算数表示如下:

sign是符号位, e是11位指数位的值, b0~b51是52位二进制尾数。举几个简单例子:

0 01111111111 0000000000000000000000000000000000000000000000000000 ≙ +1.0*2^0 = 1
0 10000000000 1000000000000000000000000000000000000000000000000000 ≙ +1.1*2^1 = 3  
复制代码

注意点:

  1. 指数部分1~2016会被表示为正常的数字

  2. 指数部分为0且尾数部分不为0用来表示非正常的数字,计算时不会有前导1, 即计算公式为:

3.指数部分为0, 尾数部分为0, 根据不同符号位值表示+0和-0

4.指数部分为2047,尾数部分为0, 根据不同符号位值表示负无穷(Number.NEGATIVE_INFINITY)、正无穷(Number.POSITIVE_INFINITY )

5.指数部分为2047, 尾数部分不为0,值会解析为NAN

疑问解答

* 0.1+0.2 !=0.3

这个问题就是浮点数存储的精度问题,我们知道尾数位只有52位,0.1用二进制表示如下:

0.000110011001100110011001100110011001100110011001100110011... 
复制代码

如上可以看出0.1二进制表示时尾数是超过52位的, 所以52位之后的会被舍去,这就有了浮点数存储的精度丢失问题。

* 安全整数范围MDN上是(-(2^53 - 1) ~ (2^53 - 1)),犀牛书上是 ( -2^53 ~ 2^53 )

1个符号位,11位指数位, 52位尾数位。按照分析,不考虑符号位,尾数位取值52个1就是表示的最大值了,不会有精度损失,此时指数位代表数值是52+1023=1075,此时即为(-(2^53 -1)~(2^53 -1) ) 这就是MDN上描述的,这个范围内的值都是没有精度问题的。但是2^53这个值,存储的时候尾数是52个0, 指数位为53+1023=1076,这个值也是刚好没有精度损失的,此时即为(-2^53 ~2^53) ,这就是犀牛书上的描述了。值得注意的是用Math.isSafeInteger()判断安全数范围和MDN中描述一样。

* js中能表示的最大正数

由IEEE754浮点数标准可知, 指数位取:2046, 尾数位取52位1就表示最大值1.7976931348623157*10^308

* js中能表示的最小正数

由IEEE754浮点数标准可知, 指数位全为:0, 尾数位取51位0, 最后一位1就表示最小值 如下图所示,表示出了最大正数与最小正数的以及一些特殊值的64位二进制存储。 附一个一些特殊值的64位存储:wiki

总结

掌握IEEE754浮点数标准,就能够深层次的理解精度、最大值、最小值以及其他问题, 需要注意IEEE754浮点数标准中的一些特殊情况。

转载于:https://juejin.im/post/5ca1cd3ae51d4518d22c04c1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值