一、既定事实:
- JavaScript提供的有效数字最长为53个二进制位。1+11+52。 整数的表示范围就是 -2^53 到 2^53 之间(不含两个端点)
- 在JS中能够表示的最大安全整数的范围是:-9007199254740991 ~ 9007199254740991。这一点可以通过Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER来求证。
两个存在的问题:
- 浮点数在四则运算中存在精度丢失的问题,比如:
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
。
问题一:
计算机中用二进制来存储小数,而大部分小数转成二进制之后都是无限循环的值,因此存在取舍问题,也就是精度丢失。
问题二:
53位有效数字都存储满了之后,想要表示更大的数字,就只能往指数数加一位,这时候尾数因为没有多余的存储空间,因此只能补0。
而最后一位尾数为为1的数字都不能被精确表示。也就是可以被精确表示和不能被精确表示的比例是1:1。
二、总结
可以发现,不管是浮点数计算的计算结果错误和大整数的计算结果错误,最终都可以归结到JS的精度只有53位(尾数只能存储53位的有效数字)。
业务场景简单处理
问题 | 解决方法 | 知识补充 | |
---|---|---|---|
浮点数参与运算丢失精度问题36.8 *100 = 3679.9999999999995 | Number().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 类型后把小数点移除
类库 | 类库地址 | 优势 | 劣势 | 单位(✨) |
---|---|---|---|---|
big | github.com/MikeMcl/big… | 较小有 3 kb,靠谱 | star数较少 | 4星 |
decimal | github.com/MikeMcl/dec… | 靠谱 | star数较少,资源 33 k | 3.5星 |
bignumber | github.com/MikeMcl/big… | 靠谱 | star数较少,资源 23 k | 3.5星 |
number-precision | github.com/nefe/number… | 非常小只有1kb | star数较少,超过JS安全数运算出错 | 2星 |
math | github.com/josdejong/m… | 功能强大,很灵活 | 资源较大,143 kb | 1星 |