openjdk8中的舍入问题

Posted

技术标签:

【中文标题】openjdk8中的舍入问题【英文标题】:Rounding issue in openjdk8 【发布时间】:2019-10-15 12:48:12 【问题描述】:

我在 open jdk1.8 中使用 NumberFormat.format(double) 时遇到了一些奇怪的问题。 我知道在此版本中修复了之前版本中的舍入问题,但仍然似乎有些不对劲。

public static void main (String args[]) 
    double num = 16.34625;

    NumberFormat nf = NumberFormat.getInstance();
    nf.setGroupingUsed(false);
    nf.setMinimumFractionDigits(2); 
    nf.setMaximumFractionDigits(4);

            System.out.println("nf : " + nf.getRoundingMode());
            System.out.println("BigDecimal is " + new  BigDecimal(num).toString());     

    System.out.println("num after number format : " + nf.format(num));

这是显示的实际结果:

nf : HALF_EVEN
num : 16.34625
BigDecimal is 16.346250000000001278976924368180334568023681640625
num after number format : 16.3463

但是,我希望它被四舍五入到 16.3462,因为在 HALF_EVEN 模式下,被丢弃的数字左边的数字是偶数,所以它应该充当 HALF_DOWN。

这是支持此操作的 java 文档 (https://docs.oracle.com/javase/7/docs/api/java/math/RoundingMode.html#HALF_EVEN)。

【问题讨论】:

使用 HALF_EVEN 舍入时,它会舍入到最近的偶数 如果它与两个邻居的距离相等。但是 16.346250000000001278976924368180334568023681640625 比 16.3462 更接近 16.3463。你有一个事实,浮点数和双精度数不能准确地表示十进制数,这要感谢。尝试从您的数字的字符串表示形式(即“16.34625”)创建 BigDecimal 以获得所需的行为。 只需将double num = 16.34625; 替换为String num = "16.34625";,看看会发生什么…… 对不起,我还是有点困惑。我同意双 16.34625 的最接近表示是 16.346250000000001278976924368180334568023681640625。但是,由于小数点后的第5位是等距的'5',那么由于它的前一位是偶数的'4',它应该充当half_down,最终结果应该是16.3462。如果前面的数字是奇数,那么它应该作为 half_up。不对吗? 【参考方案1】:

您可以尝试在BigInteger 本身中四舍五入。您可以使用该类的setScale 方法。

System.out.println(new BigDecimal(num).setScale(4,BigDecimal.ROUND_HALF_EVEN));
//16.3463

【讨论】:

我不希望得到某个数字的四舍五入。我只是想知道为什么会出现这种行为。【参考方案2】:

你说的都是对的。唯一的问题是,您的 BigDecimal 不是 16.34625,而是 16.346250000000001278976924368180334568023681640625。所以邻居不是等距的。

【讨论】:

以上是关于openjdk8中的舍入问题的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 Shapely 中的舍入误差

mysql 中的舍入不能按预期工作

有没有办法防止opencv矩阵除法中的舍入

c集成程序中的舍入错误

openjdk8 for Android 7.1.2 版本

std::cbrt 的舍入错误?