滴!!!您的银行卡已丢失2分钱

84 阅读2分钟

业务场景

在业务开发中很可能你会面临如下类似的场景。

  1. 计算员工的工资
  2. 处理银行或与银行对接的业务
  3. 对数值精度有很高要求的场景

面对这样的业务场景,如果使用原始的javascript运算,很可能得出与直觉不相符的结果,比如:

console.log(0.3 - 0.1) // 0.19999999999999998
console.log(0.1 * 0.2) // 0.020000000000000004
console.log(0.3 / 0.1) // 2.9999999999999996

如果你是刚接触编程的萌新,一定一脸懵逼,what fxxc,这是怎么回事呢,是我眼花了吗,我小学二年级就能算清楚的问题,对于计算能力如此强大,远超人类的计算机怎么会算不明白呢。

抽丝剥茧

因为计算机是通过二进制来进行计算的,即 0 和 1,而JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。 例如: 0.1 + 0.2 ,0.1表示为0.0001100110011001...,而0.2表示为0.0011001100110011... 而在二进制中 1 + 1 = 10,所以 0.1 + 0.2 = 0.0100110011001100... 转成10进制就近似表示为 0.30000000000000004

解决方案

  1. 使用toFixed()<不推荐>
0.123.toFixed(2) // '0.12'

可以控制小数点后几位,如果为空就用0补充,返回一个字符串。 但是,这种方式在不同浏览器中得出的值可能不相同,且部分数字得不到预计的结果,并不是执行严格的四舍五入,所以不推荐使用。

  1. 自己实现
// 加法
function mathPlus(arg1, arg2) {
   let r1, r2, m;
try {
    r1 = arg1.toString().split(".")[1].length; // 获取小数点后字符长度
} catch (error) {
   r1 = 0; // 为整数状态,r1赋0
}
try {
   r2 = arg2.toString().split(".")[1].length;
} catch (error) {
   r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2)); // 确保所有参数都为整数
   return (arg1 * m + arg2 * m) / m;
}
mathPlus(0.1, 0.2); // 0.3
mathPlus(1, 2); // 3


// 减法
function mathSubtract(arg1, arg2) {
   let r1, r2, m;
try {
   r1 = arg1.toString().split(".")[1].length;
} catch (error) {
   r1 = 0;
}
try {
   r2 = arg2.toString().split(".")[1].length;
} catch (error) {
    r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
   return ((arg1 * m - arg2 * m) / m);
}
mathSubtract(0.3, 0.1); // 0.2
mathSubtract(3, 1); // 2

// 乘法
function mathMultiply(arg1, arg2) {
   let m = 0;
   let s1 = arg1.toString();
   let s2 = arg2.toString();
try {
   m += s1.split('.')[1].length; // 小数相乘,小数点后个数相加
} catch (e) {}
try {
   m += s2.split('.')[1].length;
} catch (e) {}
return (
   (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) /
   Math.pow(10, m)
);
}
mathMultiply(0.1, 0.2); // 0.02
mathMultiply(1, 2); // 2

// 除法
function mathDivide(arg1, arg2) {
   let m1 = 0;
   let m2 = 0;
   let n1 = 0;
   let n2 = 0;
try {
   m1 = arg1.toString().split('.')[1].length;
} catch (e) {}
try {
    m2 = arg2.toString().split('.')[1].length;
} catch (e) {}
   n1 = Number(arg1.toString().replace('.', ''));
   n2 = Number(arg2.toString().replace('.', ''));

   // 将除法转换成乘法, 乘以它们的小数点后个数差
   return mathMultiply(n1 / n2, Math.pow(10, m2 - m1));
}
// > 0.2 / 0.03 => 6.666666666666667
mathDivide(0.2, 0.03); // 6.666666666666665
mathDivide(0.3, 0.1); // 3
mathDivide(3, 1); // 3

引入第三方库

  1. big.js 介绍:功能强大,内置大量函数,体积较大 链接: github.com/MikeMcl/big… star: 4.5k+

  2. decimal.js 链接: github.com/MikeMcl/dec… star: 5.7k+

  3. Math.js 介绍: 体积6k,提供了CDN 链接: github.com/josdejong/m… star: 13.5k+

  4. number-precision 介绍:体积很小,只有1k左右 链接: github.com/nefe/number… star: 3.8k+