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

Kotlin中BigDecimal remainder()方法整除校验异常问题

问题分析与解决方案

你的问题出在两个核心点上:

1. BigDecimal的equals比较会同时校验数值和精度(scale)

BigDecimal的equals()方法不仅对比数值是否相等,还会检查两个对象的**精度(scale)**是否一致。比如你计算6 % 0.5得到的余数是BigDecimal("0.0")(scale为1),而BigDecimal.ZERO的scale是0,此时用!=比较会返回true,错误触发异常。

2. 用Double构造BigDecimal存在潜在精度隐患

虽然0.5、0.25这类2的幂次小数在Double中是精确的,但BigDecimal(double)构造函数仍可能引入不可预见的精度问题(比如非2幂次的小数如0.1),用字符串构造能彻底保证数值精确性。


修复方案

方案一:用compareTo替代equals比较

compareTo()方法仅比较数值大小,忽略scale差异,只要数值相等就返回0:

it.quantity.remainder(product.toIncrementByNumber()).compareTo(BigDecimal.ZERO) != 0 -> {
    throw OperationNotAllowedException(
        message = "Quantity for product ${product.name} is invalid"
    )
}

方案二:优化toIncrementByNumber方法

改用字符串构造BigDecimal,彻底避免Double的精度隐患:

fun Product.toIncrementByNumber(): BigDecimal {
    return when (this.incrementBy) {
        IncrementByType.QUARTER_KG -> BigDecimal("0.25")
        IncrementByType.HALF_KG -> BigDecimal("0.5")
        IncrementByType.ONE_KG -> BigDecimal.ONE // 等价于BigDecimal("1"),写法更简洁
    }
}

方案三:对余数调用stripTrailingZeros()

去掉余数末尾的零后再和BigDecimal.ZERO比较,这样0.0会被转换成0,scale统一:

it.quantity.remainder(product.toIncrementByNumber()).stripTrailingZeros() != BigDecimal.ZERO -> {
    throw OperationNotAllowedException(
        message = "Quantity for product ${product.name} is invalid"
    )
}

推荐优先使用方案一+方案二,既解决当前的比较误判问题,也规避了后续潜在的精度风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:37:41