RoundingMode.HALF_DOWN 问题在 Java8
RoundingMode.HALF_DOWN issue in Java8
我正在使用 jdk 1.8.0_45,我们的测试发现了路由中的错误。
当决定四舍五入的最后一位小数为 5 时,RoundingMode.HALF_DOWN 与 RoundingMode.HALF_UP 的工作方式相同。
我发现了 RoundingMode.HALF_UP 的相关问题,但它们已在更新 40 中得到修复。我还向 oracle 提交了一个错误,但根据我的经验,它们确实没有响应。
package testjava8;
import java.math.RoundingMode;
import java.text.DecimalFormat;
public class Formatori {
public static void main(String[] args) {
DecimalFormat format = new DecimalFormat("#,##0.0000");
format.setRoundingMode(RoundingMode.HALF_DOWN);
Double toFormat = 10.55555;
System.out.println("Round down");
System.out.println(format.format(toFormat));
format.setRoundingMode(RoundingMode.HALF_UP);
toFormat = 10.55555;
System.out.println("Round up");
System.out.println(format.format(toFormat));
}
}
实际结果:
向下舍入
10.5556
围捕
10.5556
预期结果(用jdk1.7得到):
向下舍入
10.5555
围捕
10.5556
似乎是有意更改。 JDK 1.7 行为不正确。
问题是您 不能 使用 double
类型表示数字 10.55555
。它以 IEEE 二进制格式存储数据,因此当您将十进制 10.55555
数字分配给 double
变量时,您实际上得到了可以用 IEEE 格式表示的最接近的值:10.555550000000000210320649784989655017852783203125
。此数字大于 10.55555
,因此在 HALF_DOWN
模式下它被正确舍入为 10.5556
。
你可以检查一些可以用二进制精确表示的数字。例如,10.15625
(即 10 + 5/32
,因此二进制为 1010.00101
)。此数字在 HALF_DOWN
模式下四舍五入为 10.1562
,在 HALF_UP
模式下四舍五入为 10.1563
。
如果您想恢复旧行为,您可以先使用 BigDecimal.valueOf
构造函数将您的数字转换为 BigDecimal
,即 "translates a double
into a BigDecimal
, using the double
's canonical string representation":
BigDecimal toFormat = BigDecimal.valueOf(10.55555);
System.out.println("Round down");
System.out.println(format.format(toFormat)); // 10.5555
format.setRoundingMode(RoundingMode.HALF_UP);
toFormat = BigDecimal.valueOf(10.55555);
System.out.println("Round up");
System.out.println(format.format(toFormat)); // 10.5556
行为的变化记录在 the release notes of Java 8
中
When using the NumberFormat
and DecimalFormat
classes, the rounding behavior of previous versions of the JDK was wrong in some corner cases. [...]
As an example, when using the default recommended NumberFormatFormat
API form: NumberFormat nf = java.text.NumberFormat.getInstance()
followed by nf.format(0.8055d)
, the value 0.8055d is recorded in the computer as 0.80549999999999999378275106209912337362766265869140625 since this value cannot be represented exactly in the binary format. Here, the default rounding rule is "half-even", and the result of calling format() in JDK 7 is a wrong output of "0.806", while the correct result is "0.805", since the value recorded in memory by the computer is "below" the tie.
This new behavior is also implemented for all rounding positions that might be defined by any pattern chosen by the programmer (non default patterns).
我正在使用 jdk 1.8.0_45,我们的测试发现了路由中的错误。 当决定四舍五入的最后一位小数为 5 时,RoundingMode.HALF_DOWN 与 RoundingMode.HALF_UP 的工作方式相同。
我发现了 RoundingMode.HALF_UP 的相关问题,但它们已在更新 40 中得到修复。我还向 oracle 提交了一个错误,但根据我的经验,它们确实没有响应。
package testjava8;
import java.math.RoundingMode;
import java.text.DecimalFormat;
public class Formatori {
public static void main(String[] args) {
DecimalFormat format = new DecimalFormat("#,##0.0000");
format.setRoundingMode(RoundingMode.HALF_DOWN);
Double toFormat = 10.55555;
System.out.println("Round down");
System.out.println(format.format(toFormat));
format.setRoundingMode(RoundingMode.HALF_UP);
toFormat = 10.55555;
System.out.println("Round up");
System.out.println(format.format(toFormat));
}
}
实际结果: 向下舍入 10.5556 围捕 10.5556
预期结果(用jdk1.7得到): 向下舍入 10.5555 围捕 10.5556
似乎是有意更改。 JDK 1.7 行为不正确。
问题是您 不能 使用 double
类型表示数字 10.55555
。它以 IEEE 二进制格式存储数据,因此当您将十进制 10.55555
数字分配给 double
变量时,您实际上得到了可以用 IEEE 格式表示的最接近的值:10.555550000000000210320649784989655017852783203125
。此数字大于 10.55555
,因此在 HALF_DOWN
模式下它被正确舍入为 10.5556
。
你可以检查一些可以用二进制精确表示的数字。例如,10.15625
(即 10 + 5/32
,因此二进制为 1010.00101
)。此数字在 HALF_DOWN
模式下四舍五入为 10.1562
,在 HALF_UP
模式下四舍五入为 10.1563
。
如果您想恢复旧行为,您可以先使用 BigDecimal.valueOf
构造函数将您的数字转换为 BigDecimal
,即 "translates a double
into a BigDecimal
, using the double
's canonical string representation":
BigDecimal toFormat = BigDecimal.valueOf(10.55555);
System.out.println("Round down");
System.out.println(format.format(toFormat)); // 10.5555
format.setRoundingMode(RoundingMode.HALF_UP);
toFormat = BigDecimal.valueOf(10.55555);
System.out.println("Round up");
System.out.println(format.format(toFormat)); // 10.5556
行为的变化记录在 the release notes of Java 8
中When using the
NumberFormat
andDecimalFormat
classes, the rounding behavior of previous versions of the JDK was wrong in some corner cases. [...]As an example, when using the default recommended
NumberFormatFormat
API form:NumberFormat nf = java.text.NumberFormat.getInstance()
followed bynf.format(0.8055d)
, the value 0.8055d is recorded in the computer as 0.80549999999999999378275106209912337362766265869140625 since this value cannot be represented exactly in the binary format. Here, the default rounding rule is "half-even", and the result of calling format() in JDK 7 is a wrong output of "0.806", while the correct result is "0.805", since the value recorded in memory by the computer is "below" the tie.This new behavior is also implemented for all rounding positions that might be defined by any pattern chosen by the programmer (non default patterns).