前言
在前端项目中,经常会遇到运算精度丢失计算不准确问题,一般在比较大的项目中(尤其是跟数据挂钩像金融、电商项目)不能直接用js的运算,需要解决精度丢失问题。
常见的js浮点数运算问题
console.log(0.1 + 0.2); // 输出结果:0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // 输出结果:false
console.log(9999999999999999 === 10000000000000001); // 输出结果:true
console.log(1.345.toFixed(2)); // 输出结果:1.34 (四舍五入失效)
// 后端接口数据id返回大数据number类型(解决方案:最好让后端返回string类型)
let a = 421234567891234567;
console.log(String(a)) // 输入结果:421234567891234560
js浮点数丢失精度的原因
JS的基础类型Number,遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。 计算机编程语言里浮点数计算会存在精度丢失问题(或称舍入误差),其根本原因是计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示,如圆周率 3.1415926...,1.3333... 等。 比如(十进制小数对应的二进制表示):
// 十进制转为二进制:整数部分除以2取余,余数往左添加,小数部分乘以2取整往右添加;如101.75 >> 1100101.11
0.1 >> 0.0001 1001 1001 1001…(1001无限循环)
0.2 >> 0.0011 0011 0011 0011…(0011无限循环)
解决js浮点数精度丢失问题方法
可以自己手动封装加减乘除以及四舍五入的相关方法,也可以采用一些成熟的第三方库如decimal.js、bignumber.js、mathjs等。
这里介绍一个第三方库number-precision
,在vue项目上引用:
安装
npm install number-precision --save
使用方法
import NP from 'number-precision'
NP.strip(num) // 转为最接近的正确的数字
NP.plus(num1, num2, num3, ...) // 加法:num + num2 + num3, 至少需要2个参数
NP.minus(num1, num2, num3, ...) // 减法:num1 - num2 - num3
NP.times(num1, num2, num3, ...) // 乘法:num1 * num2 * num3
NP.divide(num1, num2, num3, ...) // 除法:num1 / num2 / num3
NP.round(num, ratio) // 根据ratio取整
用例
console.log(NP.strip(0.09999999999999998)) // 正确: 0.1
console.log(0.1 + 0.2) // 0.30000000000000004
console.log(NP.plus(0.1, 0.2)); // 正确:0.3
console.log(1.0 - 0.9); // 0.09999999999999998
console.log(NP.minus(1.0, 0.9)); // 正确 0.1
console.log(3 * 0.3); // 0.8999999999999999
console.log(NP.times(3, 0.3)); // 正确:0.9
console.log(1.21 / 1.1); // 1.0999999999999999
console.log(NP.divide(1.21, 1.1)); // 正确:1.1
console.log(1.345.toFixed(2)) // 1.34
console.log(NP.round(1.345, 2)); // 正确:1.35
关闭边界检查提示
如果你想避免下面的警告(报错:数值转换整数越界,结果不精确),可以在文档开头增加下面的提示
PS: If you want to get rid of XXX is beyond boundary when transfer to integer, the results may not be accurate, use this at the beginning of your app to turn off boundary checking.
NP.enableBoundaryChecking(false); // default param is true
参考文章
juejin.cn/post/704143…
github:github.com/nefe/number…
npm:www.npmjs.com/package/num…