缘由
最近在做公司的一个点餐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
其他第三方库
decimal.js
为 JavaScript 提供十进制类型的任意精度数值。
big.js
Big.js 是一个用于处理任意精度的大数运算的 JavaScript 库。它解决了 JavaScript 中处理大数运算时精度丢失的问题,提供了更高精度的计算能力。
Big.js 库的特点包括:
- 任意精度:Big.js 允许您处理任意精度的数字,而不受 JavaScript 内置数字类型的限制。
- 高精度计算:Big.js 提供了精确的加法、减法、乘法、除法和取余等运算,以及比较和舍入等功能。
- 可配置的精度和舍入规则:您可以自定义 Big.js 运算的精度和舍入规则,以满足特定的需求。
- 支持链式操作:您可以使用链式调用来执行多个运算,使代码更简洁易读。
- 适用于浏览器和 Node.js:Big.js 可以在浏览器和 Node.js 环境中使用,兼容性良好。
Big.js 库非常适用于需要高精度计算的场景,如金融、密码学、科学计算和大数据处理等。它允许开发人员在 JavaScript 中进行准确的数字计算,避免了精度损失带来的问题。
官网:big.js API