【最佳实践】工具类方法MathUtils.addAndCheck 使用:防止加法溢出

本文介绍了一个防止整数和长整数相加时溢出的方法,使用Apache Commons Math库中的MathUtils.addAndCheck方法,该方法在int和long类型加法中进行安全检查,避免因溢出导致的错误。

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

前言

在涉及与金融相关的计算中,有时候很容易忽略int, long 数相加时的溢出了最大边界或最小边界,如果if分支判断恰好要求两数相加小于某一个值时,而你的两数相加刚好溢出变成负数,此时就会造成不正确的运行结果,导致事故发生。

MathUtils.addAndCheck

org.apache.commons.math.util 包下已经为我们封装了两数相加的安全检查,源码理解起来也比较简单,再也不用担心溢出而出事故了。

  1. 导入包
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math</artifactId>
    <version>2.2</version>
</dependency>
  1. 使用示例
public static void main(String[] args) {
    int num1 = Integer.MAX_VALUE + Integer.MAX_VALUE;
    System.out.println(num1);
    int num2 = MathUtils.addAndCheck(Integer.MAX_VALUE, Integer.MAX_VALUE);
}

运行结果:

MathUtils.addAndCheck重载了两个方法,如下:

public static int addAndCheck(int x, int y) {
    long s = (long)x + (long)y;
    if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
        throw MathRuntimeException.createArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, x, y);
    }
    return (int)s;
}

public static long addAndCheck(long a, long b) {
    return addAndCheck(a, b, LocalizedFormats.OVERFLOW_IN_ADDITION);
}

private static long addAndCheck(long a, long b, Localizable pattern) {
    long ret;
    if (a > b) {
        // use symmetry to reduce boundary cases
        ret = addAndCheck(b, a, pattern);
    } else {
        // assert a <= b

        if (a < 0) {
            if (b < 0) {
                // check for negative overflow
                if (Long.MIN_VALUE - b <= a) {
                    ret = a + b;
                } else {
                    throw MathRuntimeException.createArithmeticException(pattern, a, b);
                }
            } else {
                // opposite sign addition is always safe
                ret = a + b;
            }
        } else {
            // assert a >= 0
            // assert b >= 0

            // check for positive overflow
            if (a <= Long.MAX_VALUE - b) {
                ret = a + b;
            } else {
                throw MathRuntimeException.createArithmeticException(pattern, a, b);
            }
        }
    }
    return ret;
}

分别是对int类型和long类型的加法检查,当发生正溢出或者负溢出时都会抛出异常。

<think>我们正在解决JavaScript中浮点数计算精度丢失的问题。根据引用,常见的解决方案是使用缩放法(乘以10的幂次方,四舍五入,再除以缩放因子)。但要注意,这种方法在数字非常大时可能会溢出,而且只能处理有限的小数位数。 然而,更全面和精确的解决方案是使用第三方库(如decimal.js、big.js)或者将数字转换为字符串进行处理。不过,根据用户提供的引用,他们倾向于使用内置的缩放方法。 我们将提供两种方法: 1. 使用缩放法(如引用[1][2]所示),并指出其局限性。 2. 推荐使用更健壮的第三方库(简要提及)。 同时,我们需要解释为什么会出现精度丢失(根据引用[3]),以便用户理解。 步骤: 1. 解释问题原因(IEEE 754双精度浮点数的限制)。 2. 提供缩放法解决方案(Math.formatFloat函数)。 3. 指出缩放法的局限性(如大数溢出、小数位数固定等)。 4. 推荐更健壮的解决方案(使用第三方库)。 注意:用户提供的Math.formatFloat函数中,Math.round函数有两个参数,但实际上Math.round只接受一个参数。因此需要修正为只传一个参数。 修正后的函数: Math.formatFloat = function (f, digit) { var m = Math.pow(10, digit); return Math.round(f * m) / m; // 去掉第二个参数,因为Math.round只接受一个参数 } 另外,我们还可以提供一种将浮点数转换为整数进行计算后再转换回来的方法(加减乘除分别处理),但考虑到通用性,我们先提供上述缩放法。 最后,生成相关问题。</think>### JavaScript 浮点数精度丢失解决方案 在 JavaScript 中,浮点数计算精度丢失是由于 IEEE 754 双精度浮点数标准的限制导致的[^3]。以下是几种有效的解决方案: #### 1. 缩放法(推荐基本方案) ```javascript /** * 浮点数精度处理函数 * @param {number} f - 要处理的浮点数 * @param {number} digit - 保留小数位数 * @returns {number} 处理后的数值 */ Math.formatFloat = function(f, digit) { const m = Math.pow(10, digit); return Math.round(f * m) / m; // 修正:Math.round只接受一个参数 }; // 使用示例 console.log(Math.formatFloat(0.1 + 0.2, 1)); // 0.3 console.log(Math.formatFloat(0.7 * 180, 0)); // 126 ``` **原理**:将浮点数放大为整数→执行计算→缩小回浮点数 **优点**:简单易用,适合基本运算 **限制**:大数可能溢出,无法解决深层精度问题[^1][^2] #### 2. 精确计算库(推荐生产环境方案) 使用专业数学库可彻底解决精度问题: ```bash # 安装 decimal.js npm install decimal.js ``` ```javascript import { Decimal } from 'decimal.js'; // 精确计算 const result = new Decimal(0.1).plus(0.2).toNumber(); // 0.3 const product = new Decimal(0.7).times(180).toNumber(); // 126 // 链式运算 const total = new Decimal(10.5) .div(3) .times(7) .toFixed(2); // "24.50" ``` **推荐库**: - `decimal.js`:最全面的解决方案 - `big.js`:轻量级替代 - `mathjs`:包含高级数学函数 #### 3. 原生改进方案 对于简单运算,可用原生方法优化: ```javascript // 精确加法 function accAdd(num1, num2) { const factor = Math.pow(10, Math.max( (num1.toString().split('.')[1] || '').length, (num2.toString().split('.')[1] || '').length )); return (num1 * factor + num2 * factor) / factor; } // 精确乘法 function accMultiply(num1, num2) { const factor = Math.pow(10, (num1.toString().split('.')[1] || '').length + (num2.toString().split('.')[1] || '').length ); return (Number(num1.toString().replace('.', '')) * Number(num2.toString().replace('.', ''))) / factor; } ``` #### 4. 显示格式化方案 仅处理显示问题,不改变实际值: ```javascript function formatDisplay(num, precision = 2) { return parseFloat(num.toPrecision(precision)); } console.log(formatDisplay(0.1 + 0.2)); // 0.3 ``` ### 不同场景推荐方案 | 场景 | 推荐方案 | 示例 | |------|----------|------| | 简单页面计算 | 缩放法 | `Math.formatFloat(0.1+0.2, 10)` | | 金融系统 | decimal.js | `new Decimal(0.1).add(0.2)` | | 显示格式化 | toPrecision | `(0.1+0.2).toPrecision(10)` | | 框架项目 | 封装工具类 | 创建`mathUtils.js`统一处理 | ### 核心问题原理 JavaScript 使用 IEEE 754 双精度浮点数标准,导致: 1. **二进制无法精确表示十进制小数** 例如 $0.1_{10} = 0.0001100110011..._2$ 2. **精度限制**:52位尾数精度,相当于15-17位十进制 3. **舍入误差累积**:连续运算放大误差[^3] $$ \text{存储值} = (-1)^{\text{sign}} \times 2^{\text{exponent}} \times \left(1 + \sum_{k=1}^{52} b_{52-k}2^{-k}\right) $$ ### 最佳实践建议 1. **避免直接比较浮点数** ```javascript // 错误 if (0.1 + 0.2 === 0.3) // 正确 function floatEqual(a, b, epsilon = 1e-10) { return Math.abs(a - b) < epsilon; } ``` 2. **整数运算优先** 将金额单位转换为"分"而非"元" 3. **后端协同处理** 复杂计算移至服务器端执行 4. **类型声明**(TypeScript) ```typescript type Currency = number; // 实际使用decimal.js类型 ``` > 引用[3]指出:浮点数精度问题本质是二进制表示限制,并非JavaScript特有缺陷[^3]。引用[1][^1][^2]提供的缩放法适合简单场景,但专业项目推荐使用decimal.js等库。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值