JavaScript如何实现精确的加减乘除运算?

一、为什么会出现精度问题?

JavaScript 采用 IEEE 754 双精度浮点数标准来表示数字。
某些小数(比如0.1、0.2)在二进制中无法精确表示,因此在运算时出现误差。

简而言之:计算机存储浮点数就像我们手动四舍五入一样,会有不可避免的舍入误差。


二、解决思路

想要避免误差,可以采取如下思路:

  • 找到小数位数最长的数

  • 将小数放大成整数进行运算

  • 最后再缩小到原来的小数位

简单来说,就是整数运算不会丢失精度,所以只需要处理好放大与缩小的过程。


三、封装加减乘除函数

首先,写一个小工具函数,获取一个数的小数位数:

function getDecimalLength(num) {
    const parts = num.toString().split('.');
    return parts[1] ? parts[1].length : 0;
}

 


1. 加法:preciseAdd

function preciseAdd(a, b) {
    const maxDecimals = Math.max(getDecimalLength(a), getDecimalLength(b));
    const factor = Math.pow(10, maxDecimals);
    return (Math.round(a * factor) + Math.round(b * factor)) / factor;
}

 


2. 减法:preciseSub

function preciseSub(a, b) {
    const maxDecimals = Math.max(getDecimalLength(a), getDecimalLength(b));
    const factor = Math.pow(10, maxDecimals);
    return (Math.round(a * factor) - Math.round(b * factor)) / factor;
}

 


3. 乘法:preciseMul

function preciseMul(a, b) {
    const aDecimals = getDecimalLength(a);
    const bDecimals = getDecimalLength(b);
    const factor = Math.pow(10, aDecimals + bDecimals);
    return (Math.round(a * Math.pow(10, aDecimals)) * Math.round(b * Math.pow(10, bDecimals))) / factor;
}

 


4. 除法:preciseDiv

function preciseDiv(a, b) {
    const aDecimals = getDecimalLength(a);
    const bDecimals = getDecimalLength(b);
    const factorA = Math.pow(10, aDecimals);
    const factorB = Math.pow(10, bDecimals);
    return (Math.round(a * factorA) / Math.round(b * factorB)) * Math.pow(10, bDecimals - aDecimals);
}

 


四、测试示例

来测试一下这几个函数的效果:

console.log(preciseAdd(0.1, 0.2)); // 输出 0.3
console.log(preciseSub(0.3, 0.1)); // 输出 0.2
console.log(preciseMul(0.1, 0.2)); // 输出 0.02
console.log(preciseDiv(0.3, 0.1)); // 输出 3

 

效果非常理想,完美解决精度问题!


五、进一步封装:PreciseMath对象

为了统一管理,可以封装成一个对象:

const PreciseMath = {
    getDecimalLength(num) {
        const parts = num.toString().split('.');
        return parts[1] ? parts[1].length : 0;
    },

    add(a, b) {
        const maxDecimals = Math.max(this.getDecimalLength(a), this.getDecimalLength(b));
        const factor = Math.pow(10, maxDecimals);
        return (Math.round(a * factor) + Math.round(b * factor)) / factor;
    },

    sub(a, b) {
        const maxDecimals = Math.max(this.getDecimalLength(a), this.getDecimalLength(b));
        const factor = Math.pow(10, maxDecimals);
        return (Math.round(a * factor) - Math.round(b * factor)) / factor;
    },

    mul(a, b) {
        const aDecimals = this.getDecimalLength(a);
        const bDecimals = this.getDecimalLength(b);
        const factor = Math.pow(10, aDecimals + bDecimals);
        return (Math.round(a * Math.pow(10, aDecimals)) * Math.round(b * Math.pow(10, bDecimals))) / factor;
    },

    div(a, b) {
        const aDecimals = this.getDecimalLength(a);
        const bDecimals = this.getDecimalLength(b);
        const factorA = Math.pow(10, aDecimals);
        const factorB = Math.pow(10, bDecimals);
        return (Math.round(a * factorA) / Math.round(b * factorB)) * Math.pow(10, bDecimals - aDecimals);
    }
};

 

调用示例:

PreciseMath.add(0.1, 0.2); // 0.3
PreciseMath.sub(0.3, 0.1); // 0.2
PreciseMath.mul(0.1, 0.2); // 0.02
PreciseMath.div(0.3, 0.1); // 3

 


六、总结

  • JavaScript 浮点数计算误差是底层机制导致的,不是 bug。

  • 将小数统一转成整数进行计算,可以完美规避误差。

  • 如果项目对数字精度要求更高,可以考虑使用第三方库:

掌握了这些技巧,你就可以更加从容地处理前端涉及的各种小数运算问题了!

posted @ 2025-04-29 09:58  曲琦  阅读(385)  评论(0)    收藏  举报