简体   繁体   English

DecimalFormat使用不正确的RoundingMode,即使显式设置

[英]DecimalFormat using incorrect RoundingMode even when set explicitly

Why is this DecimalFormat not rounding using RoundingMode.HALF_UP as expected, and what should be done to get the expected output? 为什么此DecimalFormat不能按预期使用RoundingMode.HALF_UP进行RoundingMode.HALF_UP ,应该怎么做才能获得预期的输出? Can it be done with DecimalFormat ? 可以用DecimalFormat完成吗?

DecimalFormat df = new DecimalFormat("0.0");

df.setRoundingMode(RoundingMode.HALF_UP);

df.format(0.05); // expecting 0.1, getting 0.1
df.format(0.15); // expecting 0.2, getting 0.1  << unexpected

df.format(0.06); // expecting 0.1, getting 0.0  << unexpected

I have seen the answers from this question , specifically this answer , but it only seems to work when rounding to an integer. 我已经看到了这个问题的答案,特别是这个答案 ,但是它似乎只有在四舍五入为整数时才有效。

My JDK is 8.0.110 and my JRE is 8.0.730.2 我的JDK是8.0.110,而我的JRE是8.0.730.2

(Answer below using Java 8.) (下面使用Java 8进行回答。)

The issue you are seeing comes from specifying "0.15" or "0.05" in code, which when represented as a double is something slightly less than 0.15. 您看到的问题来自在代码中指定“ 0.15”或“ 0.05”,当将其表示为double时,其值略小于0.15。 Check this out 看一下这个

DecimalFormat df = new DecimalFormat("#.#");

df.setRoundingMode(RoundingMode.HALF_UP);

BigDecimal bd = new BigDecimal(0.15);
System.out.println("bd=" + bd);
System.out.println(df.format(0.15)); // expecting 0.1, getting 0.1
bd = new BigDecimal(0.05);
System.out.println("bd=" + bd);
System.out.println(df.format(0.05));
bd = new BigDecimal(0.06);
System.out.println("bd=" + bd);
System.out.println(df.format(0.06));

The output of this code is 此代码的输出是

bd=0.1499999999999999944488848768742172978818416595458984375
0.1
bd=0.05000000000000000277555756156289135105907917022705078125
0.1
bd=0.059999999999999997779553950749686919152736663818359375
0.1

A possible solution (if you absolutely need it to round the right way) is to use BigDecimal.valueOf to create the value. 一个可能的解决方案(如果您绝对需要正确的方法)是使用BigDecimal.valueOf创建值。 For example 例如

BigDecimal bd = BigDecimal.valueOf(0.15);
System.out.println("bd=" + bd);
System.out.println(df.format(bd)); // expecting 0.1, getting 0.1
bd = BigDecimal.valueOf(0.05);
System.out.println("bd=" + bd);
System.out.println(df.format(bd));
bd = BigDecimal.valueOf(0.06);
System.out.println("bd=" + bd);
System.out.println(df.format(bd));

Will now yield 现在将屈服

bd=0.15
0.2
bd=0.05
0.1
bd=0.06
0.1

BTW, as Scary Wombat pointed out, the mask set as 0.0 instead of #.# will make 0.6 0. But I think that was a later edit then when I started looking at it. 顺便说一句,正如Scary Wombat指出的那样,将遮罩设置为0.0而不是#。#将得到0.60。但是我认为这是后来我开始查看时的后期编辑。 Use #.#. 采用 #。#。

When you do 当你做

df.format(0.15);

there are actually two rounding operations. 实际上有两个舍入运算。 The obvious one is the one you asked for with df.format , but there's an earlier one that happens at compile time. 很明显的一个是您使用df.format所要求的,但是有一个更早的版本是在编译时发生的。

The decimal value 0.15 isn't representable as a double. 十进制值0.15不能表示为双精度数。 Doubles can only represent rationals whose denominator is a power of two, just like decimal notation can only represent rationals whose denominator is a power of ten. 双精度数只能表示分母为2的幂的有理数,就像十进制表示法只能表示分母为10的幂的有理数一样。 When you write 0.15 , the compiler rounds this to the closest value representable as a double, and that value happens to be 当您编写0.15 ,编译器会将此值舍入为可以表示为双精度值的最接近值,而该值恰好是

0.1499999999999999944488848768742172978818416595458984375

which df.format correctly rounds down. df.format正确df.format It's not exactly halfway between 0.1 and 0.2, so the HALF_UP rounding mode doesn't matter. 它不是精确介于0.1和0.2之间,因此HALF_UP舍入模式无关紧要。

As for 至于

df.format(0.06); // expecting 0.1, getting 0.0  << unexpected

If you're seeing that , that's a bug. 如果您看到 ,那就是一个错误。 It looks like it matches one linked by ScaryWombat , reported to be fixed in 8u40 and 8u45. 看起来它与由ScaryWombat链接的链接相匹配,据报道已在8u40和8u45中修复。 A test on Ideone , which uses 8u51, shows the correct behavior. 在测试Ideone ,它采用8u51,显示了正确的行为。 Updating your Java should resolve the issue. 更新Java应该可以解决问题。

I think the answer is IEEE Standard for Floating-Point Arithmetic (IEEE 754). 我认为答案是浮点算术的IEEE标准(IEEE 754)。 The Wiki article has a good example in the Rounding rules section. Wiki文章的舍入规则部分提供了一个很好的示例。 Also you can read section 9.1 of Introduction to Java that expends more about floating point. 您也可以阅读Java简介的9.1该节详细介绍了浮点数。 BigDecimal solves most of these short coming by storing the uncalled value inside a BigInteger, the precision and scale in separate in integer fields. BigDecimal通过将未调用的值存储在BigInteger内(精度和小数位数分别存储在整数字段中)来解决大部分此类不足。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM