利用Math.js解决JS计算小数精度丢失问题
本文分析了JavaScript中浮点数计算精度丢失的根本原因,即二进制转换截断导致的近似值存储问题。通过引入Math.js库及其BigNumber数据类型,提供了安装、封装及具体使用示例,有效解决了电商场景中商品总价计算的精度异常。
缘由
在最近开发的公司点餐H5项目中,前端需要动态计算用户所选商品的总价(单价乘以数量)以及购物车的总价格。在此过程中,发现了JavaScript浮点数计算精度不准确的问题。即使在控制台直接输入0.1+0.2,也能观察到这一现象。
// 加法
0.1 + 0.2 = 0.30000000000000004
0.7 + 0.1 = 0.7999999999999999
0.2 + 0.4 = 0.6000000000000001
// 减法
1.5 - 1.2 = 0.30000000000000004
0.3 - 0.2 = 0.09999999999999998
// 乘法
19.9 * 100 = 1989.9999999999998
0.8 * 3 = 2.4000000000000004
35.41 * 100 = 3540.9999999999995
// 除法
0.3 / 0.1 = 2.9999999999999996
0.69 / 10 = 0.06899999999999999问题的原因
为什么0.1+0.2在JS中无法得到精确结果?产生浮点数计算精度不准确的原因在于:计算机底层处理的是二进制而非十进制。十进制小数转换为二进制后往往变成无限不循环小数,而计算机浮点数的小数部分仅支持52位。因此,两者相加后转换回十进制时,结果便不再准确,加减乘除运算均遵循此原理。
| 十进制 | 二进制 |
|---|---|
| 0.1 | 0.0001 1001 1001 1001 ... |
| 0.2 | 0.0011 0011 0011 0011 ... |
| 0.3 | 0.0100 1100 1100 1100 ... |
| 0.4 | 0.0110 0110 0110 0110 ... |
| 0.5 | 0.1 |
| 0.6 | 0.1001 1001 1001 1001 ... |
因此,两者相加后得到的一串二进制数为0.0100110011001100110011001100110011001100110011001100,由于浮点数小数位的限制,该二进制数字被截断。当将其转换回十进制时,结果便变成了0.30000000000000004。
最通俗的解释
以1÷3=0.33333333......为例,数学上可以表示3无限循环,但计算机需要存储以便后续调用。由于0.333333......无限循环,无论计算机内存多大都无法完整存储,因此只能存储一个近似值。当从存储中取出使用时,就会出现精度问题。
解决方案 - Math.js
对于任意精度的计算,Math.js支持BigNumber数据类型。
安装
npm install mathjs请注意,在TypeScript项目中使用mathjs时,您还必须安装类型定义文件:npm install @types/mathjs。
封装
import * as math from 'mathjs';
export default {
// 加
add(num1,num2){
return math.add(math.bignumber(num1),math.bignumber(num2));
},
// 乘
multiply(num1,num2){
return math.multiply(math.bignumber(num1),math.bignumber(num2));
},
// 减
subtract(num1,num2){
return math.subtract(math.bignumber(num1),math.bignumber(num2));
},
// 除
divide(num1,num2){
return math.divide(math.bignumber(num1),math.bignumber(num2));
}
}使用
import math from './math.js'
let addNum = math.add(3,2); // 5
let mulNum = math.multiply(3,2); // 6
let subNum = math.subtract(3,2); // 1
let divNum = math.divide(3,2); // 1.5附:math.js大数功能
<script type="text/javascript">
// functions and constants
console.log('mathjs BigNumber eg')
print(math.add(math.bignumber(0.1), math.bignumber(0.2))) // 加法 BigNumber, 0.3
print(math.subtract(math.bignumber(1), math.bignumber(0.9))) // 减法 BigNumber, 0.1
print(math.multiply(math.bignumber(4.10), math.bignumber(100))) // 乘法 BigNumber, 0.1
print(math.ceil(math.bignumber(6.10)/ math.bignumber(0.1))); //除法 向上取整 61
print(math.floor(math.bignumber(6.10)/ math.bignumber(0.1))); //除法 向下取整 61
print(math.round(math.bignumber(6.10)/ math.bignumber(0.1))); // 除法 四舍五入 61
/**
* Helper function to output a value in the console. Value will be formatted.
* @param {*} value
*/
function print (value) {
const precision = 14
console.log(math.format(value, precision))
}
</script>总结
到此这篇关于利用Math.js解决JS计算小数精度丢失问题的文章就介绍到这了,更多相关JS计算小数精度丢失内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
