前端数据精度丢失是个常见的问题,本次记录说实话有点大可不必,毕竟这是一个前端常见的问题。
但是考虑到自己的记忆水平不够,每次再去找攻略又有点不太合适,太过浪费时间。
索性这次就记录一下,便于后续快速使用。
正文
在前端开发中,尤其是在处理财务数据或需要高精度计算的场景下,JavaScript 的浮点数运算可能会导致精度丢失。
例如,0.1 + 0.2
的结果不是 0.3
,而是 0.30000000000000004
。
原因分析
- JavaScript 使用 IEEE 754 标准来表示浮点数,这种表示方法在某些情况下会导致精度丢失。
- 浮点数的二进制表示无法精确表示某些十进制小数,从而导致计算结果不准确。
应用场景
我知道有很多可能为了方便会用使用 toFixed
或 Math.round
,对于简单的应用场景,这可能已经足够。
但是大多数我们要处理精度的场景,往往都是涉及到支付金额的计算,这些都是非常重要的场景,一定不能出现精度丢失的情况。
所以,此时我们就需要引入精度库。
市面上有常见的好几种精度库,我这里距离如下:
- decimal.js:功能最全面,适合大多数高精度计算需求。
- big.js:体积最小,适合对性能和体积有严格要求的项目。
- bignumber.js:功能强大,支持多种数据类型,适合复杂的高精度计算场景。
以上是市面上常用的三种精度库,如果你想了解更多,欢迎参考下放链接。
big.js、bignumber.js 和 decimal.js 之间的差别(翻译)
decimal.js
我这里就先介绍我自己常用的精度库,泛用性最广,使用起来也相对简单的库。
1 | npm install decimal.js |
Decimal.js 支持以下运算符的重载
运算符名称 | 运算符 |
---|---|
加法 | + |
减法 | - |
乘法 | * |
除法 | / |
取模运算 | % |
指数运算 | ** |
自增运算 | ++ |
自减运算 | – |
这些运算符被重载后,可以直接用于 Decimal 对象之间的运算,例如:
1 | const a = new Decimal('2.5'); |
以下是它的通常用法:
格式化数值
数值被处理后,不能直接展示到页面,得格式化一下,一般常用的由以下几种。
toString()
格式化为字符串。valueOf()
格式化为字符串,但是有符号零console.log((new Decimal(-0)).valueOf()) // -0
。toNumber()
格式化为Number类型,转换为原始数字的值。toFixed()
格式化为字符串类型,用法和JS中toFixed()
一样,不同的是decimal.js中的toFixed()
有第二参数,可以设置舍入的类型。
还有一些不常用的
toBinary()
格式化为二进制。toHexadecimal()
格式化为十六进制。toOctal()
格式化为八进制。
加减乘除
在计算中最经常用到就是加减乘除,下面来看一下decimal.js中的加减乘除。
有两种用法,一种是使用Decimal
类的静态方法,一种是使用Decimal
类实例方法。
- 加法
1 | Decimal.add(1, 2); // 3 |
- 减法
1 | Decimal.sub(3, 1); // 2 |
- 乘法
1 | Decimal.mul(3, 2); // 6 |
- 除法
1 | Decimal.div(6, 2); // 3 |
使用除法时候要注意 除数(分母)不能为0。
取绝对值
1 | Decimal.abs(-3); // 3 |
比较
- 大于
1 | const x = new Decimal(2); |
- 大于等于
1 | const x = new Decimal(2); |
- 小于
1 | const x = new Decimal(2); |
- 小于等于
1 | const x = new Decimal(2); |
- 等于
1 | const x = new Decimal(2); |
判断
- 是否是整数
1 | const x = new Decimal(1) |
- 是否是正数
1 | x = new Decimal(0); |
- 是否是负数
1 | x = new Decimal(-0); |
结语
有传言说这三种精度库是一个作者,其实用哪个都可以。
但是这个传言我记不清自己是在哪里看到的了,不过这里我就不去详细考证了。
于此时的我而言,能用就行。