一、数学运算
问题:0.1 + 0.2 = 0.30000000000000004
在JavaScript中,整数和浮点数都属于Number类型,它们都统一采用64位浮点数进行存储。因浮点数的精度有限,会出现精度丢失,舍入误差问题。
const operationObj = {
/**
* 处理传入的参数,不管传入的是数组还是以逗号分隔的参数都处理为数组
* @param args
* @returns {*}
*/
getParam(args) {
return Array.prototype.concat.apply([], args); // 若args为一维或二维数组[[2]], 结果都是一纬数组[2],但是三维数组,结果是二维数组...
},
/**
* 获取每个数的乘数因子,根据小数位数计算
* 1.首先判断是否有小数点,如果没有,则返回1;
* 2.有小数点时,将小数位数的长度作为Math.pow()函数的参数进行计算
* 例如2的乘数因子为1,2.01的乘数因子为100
* @param x
* @returns {number}
*/
multiplier(x) {
let parts = x.toString().split('.');
return parts.length < 2 ? 1 : Math.pow(10, parts[1].length);
},
/**
* 获取多个数据中最大的乘数因子
* 例如1.3的乘数因子为10,2.13的乘数因子为100
* 则1.3和2.13的最大乘数因子为100
* @returns {*}
*/
correctionFactor(...args) {
// let args = Array.prototype.slice.call(arguments);
let argArr = this.getParam(args);
return argArr.reduce((accum, next) => {
let num = this.multiplier(next);
return Math.max(accum, num);
}, 1);
},
/**
* 加法运算
* @param args
* @returns {number}
*/
add(...args) {
let calArr = this.getParam(args);
// 获取参与运算值的最大乘数因子
let corrFactor = this.correctionFactor(calArr);
let sum = calArr.reduce((accum, curr) => {
// 将浮点数乘以最大乘数因子,转换为整数参与运算
return accum + Math.round(curr * corrFactor);
}, 0);
// 除以最大乘数因子
return sum / corrFactor;
},
/**
* 减法运算
* @param args
* @returns {number}
*/
subtract(...args) {
let calArr = this.getParam(args);
let corrFactor = this.correctionFactor(calArr);
let diff = calArr.reduce((accum, curr, curIndex) => {
// reduce()函数在未传入初始值时,curIndex从1开始,第一位参与运算的值需要
// 乘以最大乘数因子
if (curIndex === 1) {
return Math.round(accum * corrFactor) - Math.round(curr * corrFactor);
}
// accum作为上一次运算的结果,就无须再乘以最大因子
return Math.round(accum) - Math.round(curr * corrFactor);
});
// 除以最大乘数因子
return diff / corrFactor;
},
/**
* 乘法运算
* @param args
* @returns {*}
*/
multiply(...args) {
let calArr = this.getParam(args);
let corrFactor = this.correctionFactor(calArr);
calArr = calArr.map((item) => {
// 乘以最大乘数因子
return item * corrFactor;
});
let multi = calArr.reduce((accum, curr) => {
return Math.round(accum) * Math.round(curr);
}, 1);
// 除以最大乘数因子
return multi / Math.pow(corrFactor, calArr.length);
},
/**
* 除法运算
* @param args
* @returns {*}
*/
divide(...args) {
let calArr = this.getParam(args);
let quotient = calArr.reduce((accum, curr) => {
let corrFactor = this.correctionFactor(accum, curr);
// 同时转换为整数参与运算
return Math.round(accum * corrFactor) / Math.round(curr * corrFactor);
});
return quotient;
}
};
二、保留小数位为指定位数
在JavaScript中,toFixed函数有时也会导致一些四舍五入问题。这是因为JavaScript中的数字使用IEEE 754浮点数表示,存在精度有限的问题。
如:1.525.toFixed(2) 为1.52,而不是 1.53。
/**
* 小数:四舍五入到指定位数
* @param number { Number } 小数
* @param decimalPlaces { Number } 需要保留的小数位数
* @param isRemoveTrailingZeros { Boolean } 是否去除尾部的0, // 如:3.00,为3
* @returns {number}
*/
export function roundToFixed(number, decimalPlaces = 2, isRemoveTrailingZeros = true) {
const factor = 10 ** decimalPlaces; // **是幂运算符,左边是底数,右边是指数,即 2**3 = 8
const result = Math.round(number * factor) / factor;
if (isRemoveTrailingZeros) {
return Number(result)
} else {
return Number(result).toFixed(decimalPlaces); // 若不够指定位数,自动末尾补0
}
}
其他扩展:
1. 金额数字,自动千分位逗号分隔展示
// 货币的相关处理
const IntlInstance = new Intl.NumberFormat("zh-CN", {
style: "currency",
currency: "CNY", // 人名币
currencySign: "accounting", // 设置记账方式,会自动补全2位小数点
});
console.log(IntlInstance.format(0.1+0.2)); // ¥0.30
console.log(IntlInstance.format(0.1+0.2)); // ¥1.53
console.log(IntlInstance.format(9999999888.0)); // ¥9,999,999,888.00