Java中DecimalFormat与BigDecimal的HALF_EVEN舍入结果不一致问题及解决方法
这是个很典型的数值精度问题,我来帮你拆解一下原因和解决办法:
问题原因分析
你遇到的不一致,核心在于DecimalFormat默认处理的是double类型数值,而double本身是浮点数,存在精度丢失的问题。比如你用的21.255,作为double存储时,实际值并不是精确的21.255,而是类似21.254999999999998这样的近似值;同理21.265作为double时,实际是21.265000000000002左右的数值。
而BigDecimal是通过字符串构造的,它能精确表示十进制数值,所以基于精确值做HALF_EVEN舍入时,结果和DecimalFormat基于近似double值的舍入结果自然就不一样了。
解决办法及代码示例
要让两者结果一致,关键是让DecimalFormat也基于精确的BigDecimal数值来处理,而不是依赖double。
1. 重现问题的完整代码
先看一下能复现你问题的代码:
import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DecimalFormat; public class RoundingTest { public static void main(String[] args) { // BigDecimal精确舍入 System.out.println("BigDecimal HALF_EVEN 21.255 => " + new BigDecimal("21.255").setScale(2, RoundingMode.HALF_EVEN)); System.out.println("BigDecimal HALF_EVEN 21.265 => " + new BigDecimal("21.265").setScale(2, RoundingMode.HALF_EVEN)); // DecimalFormat默认处理double DecimalFormat cdf = new DecimalFormat("#.##"); cdf.setRoundingMode(RoundingMode.HALF_EVEN); System.out.println("DecimalFormat HALF_EVEN 21.255 => " + cdf.format(21.255)); System.out.println("DecimalFormat HALF_EVEN 21.265 => " + cdf.format(21.265)); } }
运行结果:
BigDecimal HALF_EVEN 21.255 => 21.26 BigDecimal HALF_EVEN 21.265 => 21.26 DecimalFormat HALF_EVEN 21.255 => 21.25 DecimalFormat HALF_EVEN 21.265 => 21.27
2. 修正后的代码(让DecimalFormat用BigDecimal处理)
只需要把DecimalFormat的输入换成精确的BigDecimal对象,而不是double:
import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DecimalFormat; public class RoundingTestFixed { public static void main(String[] args) { // 原BigDecimal逻辑不变 System.out.println("BigDecimal HALF_EVEN 21.255 => " + new BigDecimal("21.255").setScale(2, RoundingMode.HALF_EVEN)); System.out.println("BigDecimal HALF_EVEN 21.265 => " + new BigDecimal("21.265").setScale(2, RoundingMode.HALF_EVEN)); // 修正:传入BigDecimal而非double DecimalFormat cdf = new DecimalFormat("#.##"); cdf.setRoundingMode(RoundingMode.HALF_EVEN); System.out.println("DecimalFormat HALF_EVEN 21.255 => " + cdf.format(new BigDecimal("21.255"))); System.out.println("DecimalFormat HALF_EVEN 21.265 => " + cdf.format(new BigDecimal("21.265"))); } }
运行结果就和BigDecimal完全一致了:
BigDecimal HALF_EVEN 21.255 => 21.26 BigDecimal HALF_EVEN 21.265 => 21.26 DecimalFormat HALF_EVEN 21.255 => 21.26 DecimalFormat HALF_EVEN 21.265 => 21.26
3. 解析字符串场景的注意事项
如果是从字符串解析数值后再格式化,记得给DecimalFormat设置setParseBigDecimal(true),这样parse()方法会返回BigDecimal,避免中间转double的精度损失:
DecimalFormat cdf = new DecimalFormat("#.##"); cdf.setRoundingMode(RoundingMode.HALF_EVEN); cdf.setParseBigDecimal(true); // 解析字符串得到精确的BigDecimal BigDecimal parsedValue = (BigDecimal) cdf.parse("21.255"); // 格式化输出 System.out.println("Formatted parsed value: " + cdf.format(parsedValue)); // 输出21.26
总结
只要确保DecimalFormat处理的是精确的BigDecimal数值,而非存在精度丢失的double,就能让它和BigDecimal的HALF_EVEN舍入结果保持完全一致。
内容的提问来源于stack exchange,提问作者Sumon Rahman
相关产品推荐
相关产品推荐

