解决前端计算的浮点精度问题
问题:比如1001*1.11等于110.11
但是如果用前端开发处理的话 ,因为涉及到浮点数运算(这是因为JavaScript(以及其他许多编程语言)使用IEEE 754标准来表示浮点数,导致某些十进制小数无法精确表示,从而产生精度误差),导致1001*1.11等于110.1100000000001
解决方案:
1.使用整数进行计算
将浮点数转换为整数进行计算,最后再转换回浮点数。例如,将金额以“分”为单位进行计算,而不是以“元”为单位
let a = 0.1;
let b = 0.2;
let result = (a * 10 + b * 10) / 10; // 0.3
2.使用 toFixed
方法
toFixed
方法可以将数字四舍五入到指定的小数位数,返回一个字符串。但要注意,toFixed
返回的是字符串,可能需要转换为数字。
let a = 0.1;
let b = 0.2;
let result = parseFloat((a + b).toFixed(1)); // 0.3
3.使用第三方库
有一些专门处理高精度计算的库,如 decimal.js
、big.js
或 math.js
,它们提供了更精确的数值计算功能。
// 使用 decimal.js
let a = BigInt(12345678901234567890);
let b = BigInt(98765432109876543210);
let result = a + b; // 111111111011111111100n
对比:
4. 避免直接比较浮点数
在比较浮点数时,尽量避免使用 ==
或 ===
,而是使用一个很小的误差范围(epsilon)来判断是否相等。
function areEqual(a, b, epsilon = 0.00001) {
return Math.abs(a - b) < epsilon;
}let a = 0.1 + 0.2;
let b = 0.3;
console.log(areEqual(a, b)); // true
5.使用 Math
函数
对于某些特定场景,可以使用 Math
函数来处理精度问题,例如 Math.round
、Math.floor
、Math.ceil
等。
let a = 0.1 + 0.2;
let result = Math.round(a * 10) / 10; // 0.3
6.使用 BigIn
如果涉及大整数计算,可以使用 BigInt
类型,它可以精确表示任意大小的整数。
let a = BigInt(12345678901234567890);
let b = BigInt(98765432109876543210);
let result = a + b; // 111111111011111111100n
我目前使用的是用第三方库decimal.js
的方案
1.安装
npm install decimal.js
2.封装一个js的方法
// 处理数据相乘的浮点精度问题
export function calculateProduct(num1, num2) {
num1=Number(num1)
num2=Number(num2)
const result = new Decimal(num1).times(new Decimal(num2));
return result.toNumber() || 0;
}
3.使用
let num=calculateProduct(row.PriceExclTax,row.ShortQuantity)
4.方法
-
使用
.plus()
进行加法。 -
使用
.minus()
进行减法。 -
使用
.times()
进行乘法。 -
使用
.dividedBy()
进行除法。 -
支持链式调用,代码简洁。
-
通过
Decimal.set
配置全局精度和舍入模式。
4.1 加法(Addition)
import { Decimal } from 'decimal.js';
let a = new Decimal(0.1);
let b = new Decimal(0.2);
let sum = a.plus(b); // 0.3
console.log(sum.toString()); // "0.3"
4.2 减法(Subtraction)
import { Decimal } from 'decimal.js';
let a = new Decimal(0.3);
let b = new Decimal(0.1);
let difference = a.minus(b); // 0.2
console.log(difference.toString()); // "0.2"
4.3 乘法(Multiplication)
import { Decimal } from 'decimal.js';
let a = new Decimal(0.1);
let b = new Decimal(0.2);
let product = a.times(b); // 0.02
console.log(product.toString()); // "0.02"
4.4 除法(Division)
使用 .dividedBy()
方法。
import { Decimal } from 'decimal.js';
let a = new Decimal(10);
let b = new Decimal(3);
let quotient = a.dividedBy(b); // 3.333333333
console.log(quotient.toString()); // "3.333333333"
4.5 链式调用
decimal.js
支持链式调用,可以在一行代码中完成多个运算。
import { Decimal } from 'decimal.js';
let result = new Decimal(10)
.dividedBy(3) // 10 / 3
.plus(1) // + 1
.times(2) // * 2
.toFixed(2); // 保留两位小数
console.log(result); // "8.67"
4.6 处理浮点数精度问题
decimal.js
的核心优势是解决 JavaScript 原生浮点数计算的精度问题。
import { Decimal } from 'decimal.js';
// 原生 JavaScript 的精度问题
let nativeResult = 0.1 + 0.2; // 0.30000000000000004// 使用 decimal.js 解决精度问题
let decimalResult = new Decimal(0.1).plus(0.2); // 0.3
console.log(decimalResult.toString()); // "0.3"
4.7 格式化输出
可以使用 .toFixed()
或 .toString()
方法格式化结果。
import { Decimal } from 'decimal.js';
let result = new Decimal(10).dividedBy(3); // 3.333333333
console.log(result.toFixed(2)); // "3.33"
console.log(result.toString()); // "3.333333333"
4.8 全局配置
可以通过 Decimal.set
方法全局配置精度和舍入模式
import { Decimal } from 'decimal.js';
// 设置全局精度为 10,舍入模式为四舍五入
Decimal.set({ precision: 10, rounding: Decimal.ROUND_HALF_UP });let result = new Decimal(10).dividedBy(3); // 3.333333333
console.log(result.toFixed(2)); // "3.33"
4.9 完整示例
import { Decimal } from 'decimal.js';
// 设置全局配置
Decimal.set({ precision: 10, rounding: Decimal.ROUND_HALF_UP });// 加法
let sum = new Decimal(0.1).plus(0.2); // 0.3// 减法
let difference = new Decimal(0.3).minus(0.1); // 0.2// 乘法
let product = new Decimal(0.1).times(0.2); // 0.02// 除法
let quotient = new Decimal(10).dividedBy(3); // 3.333333333// 链式调用
let result = new Decimal(10)
.dividedBy(3)
.plus(1)
.times(2)
.toFixed(2); // "8.67"console.log({ sum, difference, product, quotient, result });