处理浮点数精度缺失的运算

浮点数进行运算的时候会丢失精度(二进制无法精确表示),比如:0.1 + 0.2 !== 0.3,那么可以先转成整数得出结果再然后算出浮点数,代码如下:

/**
 * operationObj用于处理浮点数精度缺失的运算,
 * 采用转换为整型运算之后再换成浮点数的形式
 * 时间:2022-02-22
 * 
 * 来源:javascript重难点实例精讲
 * 源代码有几处没有考虑到reduce的第一个参数的特殊性,进行了修改和简化
 */
const operationObj = {

    /**
     * 处理传入的参数,不管传入的是数组还是以逗号分隔的参数都处理为数组
     * @param {*} args 
     * @returns {*}
     */
    getParam(args) {
        return Array.prototype.concat.apply([], args)
    },

    /**
     * 获取每一个数的乘数因子,根据小数位数计算
     * 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() {
        let args = Array.prototype.slice.call(arguments);
        let argArr = this.getParam(args);
        return argArr.reduce((accum, next, curIndex) => {
            //获取第一个参数的乘数因子
            if (curIndex === 1) {
                accum = this.multiplier(accum);
            }
            let num = this.multiplier(next);
            return Math.max(accum, num);
        })
    },

    /**
     * 加法运算
     * @param  {...any} args 
     * @returns {number}
     */
    add(...args) {
        let calArr = this.getParam(args);
        //获取参与运算值的最大的乘数因子
        let corrFactor = this.correctionFactor(calArr);
        return calArr.map((item) => item * corrFactor).reduce((accnum, curr) => accnum + curr) / corrFactor;
    },

    /**
     * 减法运算
     * @param  {...any} args 
     * @returns {number}
     */
    subtract(...args) {
        let calArr = this.getParam(args);
        let corrFactor = this.correctionFactor(calArr);
        return calArr.map((item) => item * corrFactor).reduce((accnum, curr) => accnum - curr) / corrFactor;
    },

    /**
     * 乘法运算
     * @param  {...any} args 
     * @returns {*} 
     */
    multiply(...args) {
        let calArr = this.getParam(args);
        let corrFactor = this.correctionFactor(calArr);
        return calArr.map((item) => item * corrFactor).reduce((accnum, curr) => Math.round(accnum) * Math.round(curr)) / Math.pow(corrFactor, calArr.length);
    },

    /**
     * 除法运算
     * @param  {...any} 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;
    }
}

console.log(operationObj.add(0.1, 0.2) === 0.3); //true
console.log(operationObj.subtract(0.1, 0.2)); //-0.1
console.log(operationObj.multiply(0.1, 0.2)); //0.02
console.log(operationObj.divide(0.1, 2)); //0.05
posted @ 2022-02-22 16:40  晨米酱  阅读(101)  评论(0编辑  收藏  举报