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
相关产品推荐
相关产品推荐

