首页 > 解决方案 > 使用 DecimalFormat 的 RoundingMode.UP 无法按预期工作

问题描述

每当有十进制值时,我都会尝试将 Double 数字四舍五入。我正在尝试以下。下面的代码片段按预期四舍五入所有其他值。

1.08 -> 2
9.5 -> 10

但将 0.08 舍入为 0 而不是 1。我在这里遗漏了什么吗?

DecimalFormat df = new DecimalFormat("0");
df.setRoundingMode(RoundingMode.UP);
Double number = 0.08;
System.out.println(df.format(number)); // expecting 1 but produces 0
    
number = 1.08;
System.out.println(df.format(number)); // correctly produces 2

标签: javadoublerounding

解决方案


这看起来像是针对 JDK8 报告的错误 JDK-8174722的表现,并且仍然以修复版本“tbd”打开(我在 JDK14 上看到了相同的行为)。在该错误报告中,错误地四舍五入0.0001到小数点后两位会导致0.00而不是0.01.

我试图通过源代码来跟踪这个计算,DecimalFormat并发现了对有效数字的多个假设,为了兼容性而保留,但显然是导致此类问题的原因。我基于源代码的预感是,如果请求精度之后的第一个数字为 0(或足够接近),则函数会向下舍入。这种行为与明确说明的文档相反。

如果它很容易修复,我认为链接的错误早就被修复了,但它似乎也是一个低优先级。

正如评论所建议的那样,使用Math.ceil()Math.floor()Math.round()函数将提供更可预测/一致的结果,并且足以满足大多数用例。

在您的评论中,您建议用户RoundingMode提供. @Andreas此评论中发布了此解决方法的应用程序以生成此字符串:BigDecimal.setScale()

// Note: this ignores Locale
BigDecimal.valueOf(number).setScale(0, RoundingMode.UP).toPlainString();

使用 a 的另一个选项DecimalFormat将允许更改区域设置,并且是:

df.format(BigDecimal.valueOf(number).setScale(0, RoundingMode.UP));

请注意,由于您将使用四舍五入,因此setScale()您不需要在DecimalFormat.


推荐阅读