js浮点数精度问题

247 阅读3分钟

带着疑问出发:为什么最大表示2^53? 为什么9007199254740992 === 9007199254740993?

1、js中number 的存储

双精度浮点数

JS中的Number是以双精度浮点数的形式计算的,双精度浮点数总共有8个字节(byte),每字节有8比特(bit-位),即 8bit/byet,所以总共占位64位。

2、number在计算机中的存储

2.1 科学计数法

 X = a*2^e,e代表位数,可为正(大于1),可为负(小于1)

举例:

a、27.0表示成二进制为:11011.0 用科学计数法表示为 1.10110 * 2^4

b、0.625表示成二进制:0.101    用科学计数法表示为 1.01∗2^−1  1.01 * 2^-1

2.2 在计算机中的存储

根据IEEE754的标准,双精度浮点数中的占位分为3个部分:符号位、指数部分、尾数部分

image.png

  • 符号位置:1位。0表示负数,1表示正数

  • 指数部分:11位。

    公式:e+指数偏移量(1023)

    e可以为正,可以为负数。

    所以按照道理指数部分表示的二进制精度范围为:

    a、最小:1023 + (-1023) =  0 ,即0.00...001(小数点后1023位)

    b、最大:1023 + 1025  =  2^11,即111...111(共1025个1)

    超出展示:Infinity

  • 尾数部分:52位。

image.png

3、结论:

3.1、最大就是2^53 - 1

十进制:9007199254740991;二进制:1 1111111111111111111111111111111111111111111111111111 长度:53;尾数部分:52位

3.2、为什么2^53 可以表示

十进制:9007199254740992;二进制:1 0000000000000000000000000000000000000000000000000000 0 长度:54; 尾数部分:53位

虽然只能表示到2^53 - 1,但是超出未必失去精度

4、失去精度是偶现的原因

image.png

原因是超出位数的二进制类十进制的“四舍五入”。十进制的四舍五入是以5为分界线,离哪边进往哪边靠近。

应用到在二进制的单个位数的舍入时,0很明显要舍去,但是1距离前后长度都是1,该如何取舍?

4.1 IEEE754规范的舍入方案

“损失的精度最小"原则

  • 首先判断精度损失(优先级最高),向上和向下都计算,精度损失最小者获胜,也就是"最近"原则.
  • 如果距离相等(即精度损失相等),那么将执行偶数判断,偶数胜出.

举例:

1.011 01 保留 3位小数是 1.011

1.01101 101 保留4位小数是1.01110

1.0110 1 保留 4位小数是1.0110 (偶数胜出)

参考文章:

1、js精度丢失问题

2、IEEE754规范的舍入方案怎么理解呢?

3、MDN-BigInt