JS中Number类型相关的问题

148 阅读5分钟

对于Number,以前只知道是JS的基本数据类型,对其在计算机中如何存储的却没有认真考虑过。这里把我疑惑的几个点集中解决一下:

  • JS中是如何表示数字的?在计算机中具体怎么存储的?写几个例子看看,如10.625、100存到计算机中是怎样的
  • Number常见的一些静态属性,如Max_VALUE的值是多少,是如何计算得来的?
  • 为什么超过2^53-1后就不能精确计算了?
  • 0.1+0.2 !== 0.3的问题以及解决方法?
  • BigInt以及为什么它可以精确表示大数?

一、JS中是如何表示数字的?

JS中不分整型、浮点型数据,统一用国际 IEEE 754 标准的64位双精度浮点数来存储数据,即1位表示符号位 + 11位表示指数位 + 52位表示有效数字位

image.png

十进制数10.625为例:其用二进制科学计数法表示为:1.010101x2^(3),易得:

  1. 其为正数,所以符号位为0;
  2. 指数是3,但是考虑到指数可能有正有负,所以规定加上1023的偏移量再保存,即1026,转化为二进制数即10000000010
  3. 有效数字是1.010101,又因为科学计数法默认是1开头的,所以只要存010101就好了,而有效数字有52位,剩下的空位的补0。【这也就是为啥52位可以表示53位有效数字的值】【这里我一开始有个疑问,为啥010101是放在最左边呢,这里不是高位嘛?要知道这里存的是小数上的有效数字,后面0的部分会被自动忽略的,所以不影响读值
  4. 综上,所存的结果是0 10000000010 0101010000000000000000000000000000000000000000000000
  5. 特别的,如果有效数字多于53位又该如何取舍呢?见后文0.1+0.2

二、Number常见的一些静态属性值的计算

  1. Number.MAX_SAFE_INTEGER :表示在 JavaScript 中最大的安全整数。所谓的安全,就是大于这个数的整数不一定可以精确表示,其值是2^53 - 1。至于怎么得来的呢?从2^53开始,在存储的时候,有效数字位数可能超过53位,即有了进位,导致值不精确了
  2. Number.MAX_VALUE即最大值,我们可能会想到全1即是最大了,但IEEE754 标准规定全1即(0/11111111111/1111111111111111111111111111111111111111111111111111表示NaN;所以最大值其实是(0/11111111110/1111111111111111111111111111111111111111111111111111),转化成10进制数即:1.11……11x2^(2^11-2-1023) = 1111……11 * 2^(971) = (2^53-1)x(2^971)

三、0.1+0.2!==0.3 的原因以及解决方法?

原因:

先看0.1在计算机中如何存储: image.png 按上面的方法进行分析:

  1. 0.1转化为二进制,0.000110011001100…… ,后面就是1100不停地循环下去了
  2. 上面数字转为科学计数法,即1.1001100……x2^(-4),所以符号位是0、指数是1023-4=1019即1111111011,而有效数字则是10011001100……11001/100……,所以第52位是1,而后面还有1001100……,那么这里就涉及到进位了
  3. 进位的规则是:就近舍入(round to nearest)、进一舍零。即11001后面是100,那最近的是1,那就进一,前面的数加1,得到……11100。
  4. 所以最终有效数字是10011001100……11010
  5. 所以计算机中存储了0/1111111011/10011001100……11010,这是不精确的
  6. 所以后期的计算中出现了误差

解决方法:如何解决0.1 + 0.2 !== 0.3的问题

  1. 将两者之差和Number.EPSILON进行比较
    function judge(num1, num2){
        return Math.abs(num1-num2) < Number.EPSILON
    }
    console.log(judge(0.1+0.2, 0.3));
  1. 同乘以10^N,再除以10^N
    (0.1 * 10 + 0.2 *10)/10 === 0.3
  1. 使用toFixed
    Number((0.1 + 0.2).toFixed(1)) === 0.3

四、BigInt为什么可以精确表示大数?

BigInt的用法有很多资料可以学习,BigInt 是一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript 中可以用 [Number]表示的最大数字。BigInt 可以表示任意大的整数。

但是为啥呢?就因为它在数字后加了一个n嘛?

Loading……

参考文章

1.javascript 双精度浮点数剖析