前端应该知道的JavaScript浮点数和大数的原理

栏目: JavaScript · 发布时间: 5年前

内容简介:不知道大家在平时的搬砖中有没有遇到过一些JavaScript数字相关的坑,比如比较经典的0.1+0.2=0.30000000000000004、JavaScript有一个Number.MAX_VALUE还有一个Number.MAX_SAFE_INTEGER等等问题。如果这些问题不了解清楚,业务开发中很有可能会出现一些很奇怪的问题。首先在开始之前需要了解一下JavaScript的number类型在计算机中是如何存储的,这也是一切问题的基础。JavaScript的数字都是number类型的,不管是整数还是浮点数

不知道大家在平时的搬砖中有没有遇到过一些JavaScript数字相关的坑,比如比较经典的0.1+0.2=0.30000000000000004、JavaScript有一个Number.MAX_VALUE还有一个Number.MAX_SAFE_INTEGER等等问题。如果这些问题不了解清楚,业务开发中很有可能会出现一些很奇怪的问题。

几个问题

先抛出几个问题

  1. 为什么0.1+0.2 != 0.3?
  2. 为什么1.005.toFixed(2)=1.00而不是1.01

  3. 为什么会有Number.MAX_VALUE和Number.MAX_SAFE_INTEGER这两个常量同时存在?

接下来就以这三个问题为目的来梳理一下来龙去脉。

双精度存储

首先在开始之前需要了解一下JavaScript的number类型在计算机中是如何存储的,这也是一切问题的基础。JavaScript的数字都是number类型的,不管是整数还是浮点数都以IEEE754双精度的格式存储在计算机中,什么是双精度呢?就是以64个bit位来存储,具体的存储格式是:

前端应该知道的JavaScript浮点数和大数的原理

前端应该知道的JavaScript浮点数和大数的原理

分别是 1个符号位+11个指数位+52个尾数位

举个例子,如果是5.5这个数字的话,则计算过程是这样的:

5.5 转二进制 =====> 101.1 科学计数法 =====> 1.011*2^2 

存入计算机: 

符号位:0 

指数位:2 加1023 =====> 1025 转二进制 =====> 10000000001 

尾数位:1.011 隐去小数点左边的1 =====> 011

存入计算机,如下图,截图来自IEEE754可视化,感兴趣可以把玩一下

前端应该知道的JavaScript浮点数和大数的原理

接下来进入第一个问题

为什么0.1+0.2 != 0.3?

在浏览器控制台可以测试一下结果:

前端应该知道的JavaScript浮点数和大数的原理

下面就剥茧抽丝的讲一下为什么会这样

0.1 转二进制 =====> 0.0001100110011001100...(1100循环)

转科学计数法 =====> 1.100110011...(1100循环) *2^-4 

数据是无限循环的,但是可供使用的尾数位却是有限的,只有52位可以使用,所以在第53位会被舍去并且进位

最终在计算机中存储如下图:

前端应该知道的JavaScript浮点数和大数的原理

类似的0.2在计算机中的存储如下图:

前端应该知道的JavaScript浮点数和大数的原理

所以最终的计算就是:

0.00011001100110011001100110011001100110011001100110011010 + 0.0011001100110011001100110011001100110011001100110011010 = 0.0100110011001100110011001100110011001100110011001100111

计算结果转换为十进制数字就是0.30000000000000004

所以就是因为,0.1和0.2在计算机中的二进制存储会让它们本身损失掉一定的精度,而它们在计算机中的二进制存储转换成十进制时已经不是真正的0.1和0.2了,相加的结果也就自然不是0.3了。

问题来了,既然0.1在计算机中的存储已经有了舍入误差,那为什么num=0.1能得到0.1呢?

可以在控制台使用toPrecision看一下0.1在不同精度下的返回

前端应该知道的JavaScript浮点数和大数的原理

可以看出来其实0.1是截断了一部分精度后得到的结果,那么这个问题就可以转化为:双精度浮点数是按什么规则来截断的呢?

在双精度浮点数的英文wiki中可以找到中可以找到这么一段话:

前端应该知道的JavaScript浮点数和大数的原理

大意是: 如果一个 IEEE 754 的双精度浮点数被转成至少含17位有效数字的十进制数字字符串,当这个字符串转回双精度浮点数时,必须要跟原来的数相同;换句话说,如果一个双精度的浮点数转为十进制的数字时,只要它转回来的双精度浮点数不变,精度取最短的那个就行。

拿0.1来举例子,0.1和0.10000000000000001转成双精度浮点数的存储是一样的,所以取最短的0.1就行了。

接下来是第2个问题

为什么1.005.toFixed(2)=1.00而不是1.01

因为在第一个问题中已经说了,一个十进制数字转为双精度浮点数然后再取出来时,跟原十进制数字可能会有误差,试一下1.005取20个精度:

前端应该知道的JavaScript浮点数和大数的原理

很明显1.005只是一个被截断后的数字,它的双精度浮点数代表的20位精度的数字是1.0049999999999998934,所以进行保留2位的四舍五入时,2位后的数字会被全部舍去。

为什么会有Number.MAX_VALUE和Number.MAX_SAFE_INTEGER这两个常量同时存在?

可以在控制台看一下:

前端应该知道的JavaScript浮点数和大数的原理

为什么最大安全整数是2^53-1?前面说到了JavaScript浮点数存储是52位尾数位,但是因为科学计数法小数点左侧的1会在存储时省去,所以52位尾数+省去的1位=53个可表示的位数。

二进制的53位全是1时转换为十进制既是:2^53-1=9007199254740991

哪为什么2^53-1是最大安全整数呢?比它大会怎样?

在浏览器试一下:

前端应该知道的JavaScript浮点数和大数的原理

以2^53来说明一下为什么2^53-1是最大安全整数,安全在哪里

2^53 转二进制 =====> 100000000000000000000000000000000000000000000000000000(53个0)

转为科学计数法 =====> 1.00000000000000000000000000000000000000000000000000000(53个0)*2^53 

存入计算机 =====> 尾数位只有52位所以截掉末尾的0只能存52个0 

2^53+1 转二进制 =====> 100000000000000000000000000000000000000000000000000001(52个0) 

转为科学计数法 =====> 1.00000000000000000000000000000000000000000000000000001(52个0) 

存入计算机 =====> 尾数位只有52位所以截掉末尾的1只能存52个0

可以看出来,2^53和2^53+1在计算机中的存储尾数和指数都相同,所以两个不同的数在计算机中的存储是一样的,这样就非常的不安全了。

所以2^53-1是JavaScript里面的最大安全整数。至于Number.MAX_VALUE,就是把尾数位和指数位都设为1再转为十进制就好了。

如何解决

上面的说到的浮点数运算、舍入运算、大数运算,都可以使用bignumber这个库来解决,打开链接在控制台就可以写demo。

后记

业务中很多难缠的bug往往是基础知识了解的不够深入造成的,了解这些原理可以清晰的知道自己在写什么,并且快速的debug,不至于陷入cv(control+C、control+V)工程师的循环中。


以上所述就是小编给大家介绍的《前端应该知道的JavaScript浮点数和大数的原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Web Application Hacker's Handbook

The Web Application Hacker's Handbook

Dafydd Stuttard、Marcus Pinto / Wiley / 2011-9-27 / USD 50.00

The highly successful security book returns with a new edition, completely updated Web applications are the front door to most organizations, exposing them to attacks that may disclose personal infor......一起来看看 《The Web Application Hacker's Handbook》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具