我发现java 7和java 8之间DecimalFormat
类的舍入不一致。这是我的测试用例:
import java.text.DecimalFormat;
public class DecimalFormatTest {
public static void main(String[] args) {
DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance();
format.setDecimalSeparatorAlwaysShown(true);
format.setMinimumFractionDigits(1);
format.setMaximumFractionDigits(1);
System.out.println(format.format(83.65));
}
}
在Java(TM)SE运行时环境(版本1.7.0_51-b13)中,输出为:
83.6
在Java(TM)SE运行时环境(版本1.8.0-b132)中,输出为:
83.7
这是一个回归错误吗?或者随着Java 8的发布,舍入规则发生了变化?
答案 0 :(得分:22)
看起来这是JDK 7中一个长期存在的错误,最终得到修复。例如见:
有一个计划草案,向JDK 8提供以下建议,解释了这个问题:
----------------------------------------------- ----------------------区域:核心库/ java.text
概要:修复了JDK7错误的舍入行为。该 NumberFormat / DecimalFormat format()方法的舍入行为 当价值非常接近准确地坐在一个平局时改变了 格式化模式中指定的舍入位置。
不相容的性质:行为
描述:使用NumberFormat / DecimalFormat类时, 以前的JDK版本的舍入行为在某些角落是错误的 案例。调用format()方法时发生了这种错误的行为 一个非常接近平局的值,同时指定舍入位置 通过使用的NumberFormat / DecimalFormat实例的模式 正好坐在领带的位置。在那种情况下错误的双倍 发生了舍入或错误的非舍入行为。
例如,使用默认推荐的
NumberFormatFormat
API 形式:NumberFormat nf = java.text.NumberFormat.getInstance()
紧随其后 按nf.format(0.8055d)
,值0.8055d
在计算机中记录为0.80549999999999999378275106209912337362766265869140625
因为此值无法以二进制格式精确表示。这里默认 舍入规则是“半偶数”,并且调用format()的结果 JDK7的输出错误为“0.806”,而正确的结果为“0.805” 因为计算机在存储器中记录的值是“低于”平局。此新行为也适用于所有舍入位置 可能由程序员选择的任何模式定义(非 默认的)。
RFE
7131459
答案 1 :(得分:15)
正如在此问题的其他答案中所提到的,JDK 8对问题JDK-7131459: DecimalFormat produces wrong format() results when close to a tie中的DecimalFormat
四舍五入进行了故意更改。
但是,这些更改引入了一个真正的错误,其归档为JDK-8039915: Wrong NumberFormat.format() HALF_UP rounding when last digit exactly at rounding position greater than 5。例如:
99.9989 -> 100.00
99.9990 -> 99.99
简单地说,这表明更高的数字围绕向下,而较低的数字围绕向上 :{{1 }}。它似乎只影响(x <= y) != (round(x) <= round(y))
舍入模式,这是在小学运算类中教授的一种舍入:总是从零开始0.5轮。
此问题存在于Java 8的Oracle和OpenJDK版本中,并且更新了8u5,8u11,8u20,8u25和8u31。
HALF_UP
公开GA 8u40发布release notes)感谢Holger this answer对相关问题的研究,我能够开发一个运行时补丁,我的雇主已经根据GPLv2许可的条款免费发布了它异常 1 (与OpenJDK源代码相同)。
补丁项目和源代码是hosted on GitHub,其中包含有关此错误的更多详细信息以及可下载二进制文件的链接。该补丁在运行时工作,因此不会对磁盘上的Java文件进行任何修改,并且应该可以安全地在所有版本的Oracle Java&gt; = 6上使用,至少通过版本8(包括u40及更高版本)。
1 我不是律师,但我的理解是 GPLv2 w / CPE 允许以二进制形式进行商业用途,而不适用于GPL合并后的工作。