Java NumberFormat的UP舍入模式异常,测试用例失败咨询
问题根源:Double的精度缺失导致UP舍入未按预期工作
你并没有误用UP舍入模式——这个模式确实会向远离零的方向舍入所有非零的舍弃部分。问题出在你使用的Double类型无法精确表示0.01这个数值,这才导致了不符合预期的格式化结果。
为什么会这样?
当你执行1 / 100.toDouble()时,得到的并不是精确的0.01,而是一个略小于0.01的近似值(具体是0.009999999999999998)。虽然调试时IDE可能显示为0.01,但底层存储的是这个不精确的二进制浮点值。
当NumberFormat处理这个近似值时,它看到的是一个接近0但还没到0.01的数。结合maximumFractionDigits = 0的设置,UP舍入模式会检查小数点后的部分:由于这个近似值的小数点后第一位是0(实际值是0.009999...),舍入逻辑并没有触发向整数位的进位,最终输出了$0。
解决方案:使用BigDecimal保证数值精确
要解决这个问题,你需要用BigDecimal来精确表示0.01,而不是依赖Double的近似值。修改后的测试用例如下:
@Test fun testFormatRoundedUp() { val formatter = NumberFormat.getCurrencyInstance() formatter.maximumFractionDigits = 0 formatter.roundingMode = RoundingMode.UP formatter.currency = Currency.getInstance("USD") // 使用BigDecimal精确表示0.01,避免Double精度问题 val decimal = BigDecimal("0.01") assertThat(formatter.format(decimal)).isEqualTo("$1") }
额外验证:非货币格式化的情况
如果你不用货币格式化,同样的精度问题也会存在。比如用普通的NumberFormat格式化Double类型的0.01,也会得到0;但用BigDecimal就会得到预期的1:
val formatter = NumberFormat.getInstance() formatter.maximumFractionDigits = 0 formatter.roundingMode = RoundingMode.UP // Double版本输出0 println(formatter.format(0.01)) // BigDecimal版本输出1 println(formatter.format(BigDecimal("0.01")))
总结一下:你的UP舍入模式用法是正确的,问题完全源于Double的浮点精度限制。改用BigDecimal精确表示小数后,就能得到你想要的舍入结果了。
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

