You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建BigDecimal触发Infinity NumberFormatException异常排查

异常根因

这个异常是传入currency方法的float参数为**无穷大(Infinity)或NaN(非数值)**导致的,这类值是float类型的合法取值,不会在方法调用前触发任何错误,只有执行到new BigDecimal(number)时才会抛出异常。

  • float类型遵循IEEE 754浮点数标准,原生支持三个特殊值:正无穷(Float.POSITIVE_INFINITY)、负无穷(Float.NEGATIVE_INFINITY)、非数值(Float.NaN)。这三个值本身是合法的float基础类型值,方法接收参数时不会做额外校验,自然不会提前抛出异常。只有BigDecimal的构造方法不接受这三个特殊值,会主动抛出NumberFormatException,报错信息就是你捕获到的Infinity or NaN。
  • 你在setCurrentPrice里写的前置判断if(currentPrice < 0.0001)根本拦不住这两个特殊值:
    • 按照浮点数运算规则,任何数值和NaN做<、>、==、<=、>=比较,结果一律为false,所以NaN传入时会直接走else分支调用currency方法
    • 正无穷比所有有限数值都大,POSITIVE_INFINITY < 0.0001的结果也是false,同样会进入else分支传到currency方法里
  • 线上出现这类特殊值的常见原因:服务端接口返回了非法运算结果(比如除以0得到的无穷值),Gson默认会把字符串"Infinity"解析为Float.POSITIVE_INFINITY;或者本地做价格计算时出现除以0、非法数学运算,结果直接赋值给了currentPrice字段。
修复方案
  1. 在setCurrentPrice入口加特殊值校验,从源头拦截非法值,避免脏值入库:
public void setCurrentPrice(float currentPrice) {
    // 拦截NaN、无穷大这类非法值,按业务需求给默认值
    if (Float.isNaN(currentPrice) || Float.isInfinite(currentPrice)) {
        currentPrice = 0f;
    }
    if(currentPrice < 0.0001) {
        priceName = "$" + NumberFormatter.roundToLastDecimalDigits(currentPrice, 3);
    } else {
        priceName = NumberFormatter.currency(currentPrice);
    }
    this.currentPrice = currentPrice;
}
  1. 在currency方法内部加兜底校验,同时修复原代码里的潜在索引越界问题:
public static String currency(float number) {
    // 兜底拦截非法浮点数,避免构造BigDecimal崩溃
    if (Float.isNaN(number) || Float.isInfinite(number)) {
        return "$0.00"; // 可根据业务调整默认展示值,比如"--"
    }

    NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
    currencyFormatter.setCurrency(Currency.getInstance("USD"));

    String[] floatParts = new BigDecimal(number).toPlainString().split("\\.");

    if(number < 1 && floatParts.length == 2) {
        String decimalPortion = floatParts[1];
        int numDecimalPlaces = 0;
        // 补长度判断,避免小数部分全为0时触发索引越界
        while (numDecimalPlaces < decimalPortion.length() && decimalPortion.charAt(numDecimalPlaces) == '0')
            numDecimalPlaces++;

        currencyFormatter.setMaximumFractionDigits(numDecimalPlaces > 2 ? numDecimalPlaces + 2 : 2);
    } else {
        currencyFormatter.setMaximumFractionDigits(2);
    }

    return currencyFormatter.format(number);
}

注意:原代码里遍历小数部分前置0的逻辑没有做长度边界判断,如果传入0.0000这类小数部分全为0的值,charAt会访问到字符串外的索引,触发StringIndexOutOfBoundsException,上面的修复代码已经补上了这个判断。

内容的提问来源于stack exchange,提问作者AtomicallyBeyond

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:07:04