使用 big.js 解决 js 小数精度问题
Published in:2024-06-08 | category: 前端 解决方案

一、背景

当涉及到浮点数计算时,js 中的精度丢失问题, 是由于使用 IEEE 754 标准来表示和计算浮点数的方式引起的。这个问题不仅仅在 js 中存在,而是在所有使用 IEEE 754 标准的编程语言中都会遇到。
IEEE 754 标准定义了两种常见的浮点数表示形式:单精度(32 位)和双精度(64 位)。在 js 中,采用的是双精度表示法,即 64 位。
然而,由于二进制和十进制之间的转换存在差异,某些十进制分数无法精确表示为有限位的二进制浮点数。这导致了舍入误差和精度丢失。

二、解决方案

使用 big.js 进行计算,而不是直接加减乘除

安装依赖

1
npm install --save big.js

方法封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Big from 'big.js'

export function createFactory(method = 'add') {
return function (...nums) {
// 将传入的参数转换为Number类型,并过滤掉不是Number类型的结果
nums = nums.map(Number).filter((num) => num || num === 0)
// 如果过滤后的结果是长度为1的数组,那就返回数组的第一项
// 如果过滤后的结果为空数组,则返回0
if (nums.length < 2) return nums[0] || 0
// 需要为reduce方法赋初值,是因为big.js的运算操作,是基于new Big格式的数字
// 可以将Big对象转换为浮点数,方便后续Number.toFixed()的操作
return parseFloat(nums.slice(1).reduce((prev, num) => prev[method](num), new Big(nums[0]))) || 0
}
}

`plus、minus、times、div为big.js中的计算方法,分别对应加减乘除这四个运算操作`

`浮点数求和`
export const secureAdd = createFactory('plus')
`浮点数相减`
export const secureSub = createFactory('minus')
`浮点数相乘`
export const secureMul = createFactory('times')
`浮点数相除`
export const secureDiv = createFactory('div')
  • 测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { secureAdd, secureSub, secureMul, secureDiv } from '@/utils/calculate'

mounted() {
this.calTestHandler()
},

methods: {
calTestHandler() {
const operations = [
{ operator: '+', method: secureAdd, a: 0.1, b: 0.2 },
{ operator: '-', method: secureSub, a: 0.1, b: 0.3 },
{ operator: '*', method: secureMul, a: 0.1, b: 0.2 },
{ operator: '/', method: secureDiv, a: 0.1, b: 0.3 }
]

operations.forEach((operation) => {
const { operator, method, a, b } = operation
const result = method(a, b)
console.log(`原生js ${operator} 运算:${a} ${operator} ${b}的值是${eval(a + operator + b)}`)
console.log(`big.js ${operator} 运算:${a} ${operator} ${b}的值是${result}`)
})
}
}

结果展示:

Prev:
浏览器
Next:
21 个让 React 项目更整洁的最佳实践