RoundingMode.FLOOR模式下BigDecimal与DecimalFormat舍入结果不一致问题
// 问题复现代码 BigDecimal bd = new BigDecimal(-0.0009).setScale(0, RoundingMode.FLOOR); System.out.println(bd.toPlainString()); // 输出-1 DecimalFormat df = new DecimalFormat("0"); df.setRoundingMode(RoundingMode.FLOOR); System.out.println(df.format(-0.0009)); // 输出-0
差异原因
RoundingMode.FLOOR的官方定义是向负无穷方向舍入,-0.0009介于-1和0之间,向负无穷舍入的正确结果应为-1,BigDecimal的输出完全符合规范。DecimalFormat输出-0是其内部实现逻辑导致的:
- 它在应用舍入规则前,会先根据设置的格式模式的小数位数,对输入值做预精度截断。当使用
0、0.0、0.00这类小数位少于3的模式时,-0.0009会被先近似识别为-0,后续对-0应用FLOOR舍入时,输出结果仍为-0。 - 当使用
0.000模式时,小数位为3位,-0.0009不会被预截断为-0,会保留足够精度参与舍入,因此能得到符合预期的结果。
解决方案
- 方案1:优先使用
BigDecimal完成舍入逻辑,再将舍入后的结果传给DecimalFormat格式化,规避DecimalFormat的内部逻辑问题,示例代码:
BigDecimal bd = new BigDecimal("-0.0009").setScale(0, RoundingMode.FLOOR); DecimalFormat df = new DecimalFormat("0"); System.out.println(df.format(bd)); // 输出-1,符合预期
- 方案2:若必须使用
DecimalFormat的舍入能力,需开启setParseBigDecimal配置,且传入字符串构造的BigDecimal类型参数,避免double精度丢失和预截断问题,示例代码:
DecimalFormat df = new DecimalFormat("0"); df.setRoundingMode(RoundingMode.FLOOR); df.setParseBigDecimal(true); System.out.println(df.format(new BigDecimal("-0.0009"))); // 输出-1,符合预期
注意:直接用double值构造
BigDecimal存在精度丢失风险,推荐优先使用字符串形式的构造方法传入精确数值。
内容的提问来源于stack exchange,提问作者Tyronne
相关产品推荐
相关产品推荐

