JS浮点数精度问题

276 阅读3分钟

一、既定事实:

  1. JavaScript提供的有效数字最长为53个二进制位。1+11+52。 整数的表示范围就是 -2^53 到 2^53 之间(不含两个端点)
  2. 在JS中能够表示的最大安全整数的范围是:-9007199254740991 ~ 9007199254740991。这一点可以通过Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER来求证。

两个存在的问题:

  1. 浮点数在四则运算中存在精度丢失的问题,比如: 01 + 0.2 // 0.30000000000000004。

JavaScript 内部只有一种数字类型Number,也就是说,JavaScript 语言的底层根本没有整数,所有数字都是以IEEE-754标准格式64位浮点数形式储存,1与1.0是相同的。\

计算机中用二进制来存储小数,但是有些小数以二进制表示位数是无穷的。 JavaScript会把超出53位之后的二进制舍弃,所以涉及小数的比较和运算要特别小心。典型的例子:1. 著名的 0.1 + 0.2 === 0.30000000000000004

2进制计算,得到2进制的数据,截断之后的二进制数字再转换为十进制,失去精度了 3. 超过最大安全整数的运算是不安全的,比如:9007199254740991 + 2 // 9007199254740992

问题一:

km.sankuai.com/page/398109…

计算机中用二进制来存储小数,而大部分小数转成二进制之后都是无限循环的值,因此存在取舍问题,也就是精度丢失。

问题二:

53位有效数字都存储满了之后,想要表示更大的数字,就只能往指数数加一位,这时候尾数因为没有多余的存储空间,因此只能补0。

而最后一位尾数为为1的数字都不能被精确表示。也就是可以被精确表示和不能被精确表示的比例是1:1。

二、总结

可以发现,不管是浮点数计算的计算结果错误和大整数的计算结果错误,最终都可以归结到JS的精度只有53位(尾数只能存储53位的有效数字)。

业务场景简单处理

问题解决方法知识补充
浮点数参与运算丢失精度问题36.8 *100 = 3679.9999999999995Number().toFixed()toFixed(num) 方法可把 Number 四舍五入为指定小数位数的数字num取值为0-20,忽略为0。就是个整数了。从浮点数角度问题:其实不会丢失的,但是从考虑到万一数字超过number最大值253-1,可以用toFixed()处理下。toFixed(d):也是四舍五入,可以任意小数位数四舍五入,返回的是字符串,必须先转为数字才能计算。Math.round(num):四舍五入,只能取整数,返回的是数字,num可以是串,可直接做加减。
Math.round()4舍5入的整数。

三、三方库

基本思想:将浮点数乘以10的n次幂(n 为小数位数),之后进行数字运算,运算结束后在除以10的n次幂

注意:直接计算浮点数与10的n次幂相乘还会出现精度丢失问题,所以需要通过转换为 String 类型后把小数点移除

类库类库地址优势劣势单位(✨)
biggithub.com/MikeMcl/big…较小有 3 kb,靠谱star数较少4星
decimalgithub.com/MikeMcl/dec…靠谱star数较少,资源 33 k3.5星
bignumbergithub.com/MikeMcl/big…靠谱star数较少,资源 23 k3.5星
number-precisiongithub.com/nefe/number…非常小只有1kbstar数较少,超过JS安全数运算出错2星
mathgithub.com/josdejong/m…功能强大,很灵活资源较大,143 kb1星

四、参考

km.sankuai.com/page/101188…