解决js加减乘除精度丢失问题

文章介绍了JavaScript中的四个函数:toNonExponential用于将科学计数法转换为常规字符串,以及floatMultiply、floatDivide、floatAdd和floatSub进行浮点数乘除和加减运算,确保结果精度一致。这些函数在处理数值计算时,特别关注数字格式的转换和精度控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

公共类, 将科学计数法的数字转为字符串(以下加减乘除依赖该方法)

var toNonExponential = (num)=> {
    if(num == null) {
        return num;
    }
    if(typeof num == "number") {
        var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);
        return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
    }else {
        return num;
    }
 }

乘(以下加减除依赖该方法)

var floatMultiply = (arg1, arg2) => {
    arg1 = Number(arg1);
    arg2 = Number(arg2);
    if ((!arg1 && arg1!==0) || (!arg2 && arg2!==0)) {
        return null;
    }
    arg1 = toNonExponential(arg1);
    arg2 = toNonExponential(arg2);
    var n1, n2;
    var r1, r2; // 小数位数
    try {
        r1 = arg1.toString().split(".")[1].length;
    } catch (e) {
        r1 = 0;
    }
    try {
        r2 = arg2.toString().split(".")[1].length;
    } catch (e) {
        r2 = 0;
    }
    n1 = Number(arg1.toString().replace(".", ""));
    n2 = Number(arg2.toString().replace(".", ""));
    return n1 * n2 / Math.pow(10, r1 + r2);
}

var floatDivide = (arg1, arg2) => {
    arg1 = Number(arg1);
    arg2 = Number(arg2);
    if (!arg2) {
        return null;
    }
    if (!arg1 && arg1!==0) {
        return null;
    }else if(arg1===0) {
        return 0;
    }
    arg1 = toNonExponential(arg1);
    arg2 = toNonExponential(arg2);
    var n1, n2;
    var r1, r2; // 小数位数
    try {
        r1 = arg1.toString().split(".")[1].length;
    } catch (e) {
        r1 = 0;
    }
    try {
        r2 = arg2.toString().split(".")[1].length;
    } catch (e) {
        r2 = 0;
    }
    n1 = Number(arg1.toString().replace(".", ""));
    n2 = Number(arg2.toString().replace(".", ""));
    return floatMultiply((n1 / n2), Math.pow(10, r2 - r1));
}

var floatAdd = (arg1, arg2) => {
    arg1 = Number(arg1) || 0;
    arg2 = Number(arg2) || 0;
    arg1 = toNonExponential(arg1);
    arg2 = toNonExponential(arg2);
    var r1, r2, m;
    try {
        r1 = arg1.toString().split(".")[1].length;
    } catch (e) {
        r1 = 0;
    }
    try {
        r2 = arg2.toString().split(".")[1].length;
    } catch (e) {
        r2 = 0;
    }
    m = Math.pow(10, Math.max(r1, r2));
    return (floatMultiply(arg1, m) + floatMultiply(arg2, m)) / m;
}

var floatSub = (arg1, arg2) => {
    arg1 = Number(arg1) || 0;
    arg2 = Number(arg2) || 0;
    arg1 = toNonExponential(arg1);
    arg2 = toNonExponential(arg2);
    var r1, r2, m, n;
    try {
        r1 = arg1.toString().split(".")[1].length;
    } catch (e) {
        r1 = 0;
    }
    try {
        r2 = arg2.toString().split(".")[1].length;
    } catch (e) {
        r2 = 0;
    }
    m = Math.pow(10, Math.max(r1, r2));
    // 动态控制精度长度
    n = (r1 >= r2) ? r1 : r2;
    return ((floatMultiply(arg1, m) - floatMultiply(arg2, m)) / m).toFixed(n);
}

参考:https://blog.youkuaiyun.com/ycclydy/article/details/123580326

<think>好的,我现在需要解决用户关于JavaScript加减乘除运算精度丢失问题。用户提到的问题比如0.3*1=0.2999999999这样的错误,这可能是因为浮点数在二进制中的表示问题导致的。我需要根据提供的引用资料来整理解决方案。 首先,引用[3]提到可以将小数转换为整数进行计算,然后再调整回原来的倍数。例如,在加减乘除运算前,将小数乘以10的幂次方转换成整数,运算后再除以相应的倍数。这样可以避免浮点数的精度问题。例如,0.1 + 0.2可以转换为(1 + 2)/10 = 0.3。 引用[2]和[4]则提到了使用toFixed方法来保留指定位数的小数,但需要注意这可能并不总是可靠,因为toFixed本身也可能存在四舍五入的问题。比如,0.085.toFixed(2)可能会得到正确的结果,但如果是0.1.toFixed(20)可能会有多余的零。 另外,引用[1]和[3]都提到了处理乘除时需要特别处理倍数。例如,乘法需要将两个数的倍数相乘,而除法则需要调整倍数以确保精度。 接下来,我需要将这些方法具体化,给出加减乘除的函数实现。例如,加法函数需要确定两个数的小数位数,取较大的位数作为倍数,将两个数都转换为整数进行运算,最后再除以倍数。同样的,减法也需要类似的处理。 乘法和除法则需要更复杂的处理。乘法需要将两个数的倍数相乘,然后结果除以这个乘积的倍数。而除法则需要将被除数乘以倍数,然后除以除数,可能需要调整倍数来保持精度。 另外,用户可能需要一个四舍五入的函数,比如保留指定位数的小数,避免出现0.3000000004这样的情况。可以使用Math.round结合指数运算来实现。 最后,需要提醒用户注意输入必须为数字,避免NaN的情况,并且处理大数时可能的溢出问题。例如,使用指数法时,如果小数位数过多,可能会导致整数超出JavaScript的安全范围(2^53),从而引发新的精度问题。 总结步骤: 1. 转换为整数计算:加减乘除都先放大为整数,计算后再缩小。 2. 使用toFixed处理显示,但注意其局限性。 3. 实现具体的加减乘除函数,处理不同位数的小数。 4. 提供四舍五入的函数,确保结果精确到指定位数。 5. 注意事项:输入验证、溢出问题等。 现在需要将这些步骤整理成清晰的解决方案,并给出代码示例和相关问题。</think>### JavaScript解决加减乘除精度丢失的方案 由于JavaScript使用IEEE 754标准的双精度浮点数表示小数,导致如$0.1 + 0.2 = 0.30000000000000004$的精度问题[^4]。以下是具体解决方案: --- #### 一、核心思路:整数化计算 将小数转换为整数进行运算,最后再调整倍数。例如: $$ \text{计算} \ 0.1 + 0.2 \ \Rightarrow \ (1 \times 10 + 2 \times 10) / 10 = 0.3 $$ 实现步骤如下: 1. **确定放大倍数**:根据小数位数计算$10^n$ 2. **转换为整数**:小数乘以倍数后取整 3. **数学运算**:对整数执行加减乘除 4. **缩小回原倍数**:结果除以对应倍数 --- #### 二、具体实现代码 ##### 1. 加法 ```javascript function add(a, b) { const maxDigits = Math.max( (a.toString().split('.')[1] || '').length, (b.toString().split('.')[1] || '').length ); const multiplier = 10 ** maxDigits; return (Math.round(a * multiplier) + Math.round(b * multiplier)) / multiplier; } ``` ##### 2. 减法 ```javascript function subtract(a, b) { return add(a, -b); // 复用加法逻辑 } ``` ##### 3. 乘法 ```javascript function multiply(a, b) { const aDigits = (a.toString().split('.')[1] || '').length; const bDigits = (b.toString().split('.')[1] || '').length; const multiplier = 10 ** (aDigits + bDigits); return (a * (10 ** aDigits)) * (b * (10 ** bDigits)) / multiplier; } ``` ##### 4. 除法 ```javascript function divide(a, b) { const aDigits = (a.toString().split('.')[1] || '').length; const bDigits = (b.toString().split('.')[1] || '').length; const multiplier = 10 ** (aDigits - bDigits); return (a * (10 ** aDigits)) / (b * (10 ** bDigits)) * multiplier; } ``` --- #### 三、辅助方法:精度截断 使用`toFixed`结合数值转换: ```javascript function round(value, digits) { return Number(Math.round(value + 'e' + digits) + 'e-' + digits); } console.log(round(0.1 + 0.2, 2)); // 输出0.3 ``` --- #### 四、注意事项 1. **输入验证**:确保参数为有效数字,避免`NaN` 2. **大数溢出**:放大后的整数不应超过$2^{53}$(JavaScript安全整数范围) 3. **性能优化**:频繁计算时建议使用第三方库如`decimal.js` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸鱼起码是条鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值