当前位置: 首页 > 面试题库 >

Java 为什么Math.round(0.49999999999999994)返回1?

解修然
2023-03-14
问题内容

在下面的程序中,你可以看到.5除以外的每个值都略小于四舍五入的值0.5。

for (int i = 10; i >= 0; i--) {
    long l = Double.doubleToLongBits(i + 0.5);
    double x;
    do {
        x = Double.longBitsToDouble(l);
        System.out.println(x + " rounded is " + Math.round(x));
        l--;
    } while (Math.round(x) > i);
}

版画

10.5 rounded is 11
10.499999999999998 rounded is 10
9.5 rounded is 10
9.499999999999998 rounded is 9
8.5 rounded is 9
8.499999999999998 rounded is 8
7.5 rounded is 8
7.499999999999999 rounded is 7
6.5 rounded is 7
6.499999999999999 rounded is 6
5.5 rounded is 6
5.499999999999999 rounded is 5
4.5 rounded is 5
4.499999999999999 rounded is 4
3.5 rounded is 4
3.4999999999999996 rounded is 3
2.5 rounded is 3
2.4999999999999996 rounded is 2
1.5 rounded is 2
1.4999999999999998 rounded is 1
0.5 rounded is 1
0.49999999999999994 rounded is 1
0.4999999999999999 rounded is 0

我正在使用Java 6 update 31。


问题答案:

摘要

在Java 6(可能更早)中,round(x)实现为floor(x+0.5)。1 这是一个规范错误,恰恰是这种病理情况。2 Java 7不再强制执行此无效的实现。3

问题

0.5 + 0.499999999999999999994的双精度正好为1:

static void print(double d) {
    System.out.printf("%016x\n", Double.doubleToLongBits(d));
}

public static void main(String args[]) {
    double a = 0.5;
    double b = 0.49999999999999994;

    print(a);      // 3fe0000000000000
    print(b);      // 3fdfffffffffffff
    print(a+b);    // 3ff0000000000000
    print(1.0);    // 3ff0000000000000
}

这是因为0.49999999999999994的指数小于0.5,因此当添加它们时,尾数会移动,并且ULP会变大。

解决方案

从Java 7开始,例如,OpenJDK实现了它:4

public static long round(double a) {
    if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5
        return (long)floor(a + 0.5d);
    else
        return 0;
}


 类似资料:
  • 问题内容: 为什么此代码有时返回1E + 1,而对于其他输入(例如17)却没有以科学计数法打印输出? 问题答案: 使用bigDecimal.toPlainString(): 输出:

  • 问题内容: 即使正则表达式应返回true,程序也不会退出。代码有什么问题? 问题答案: 仅当整个字符串与正则表达式匹配时才返回。在你的情况下,你的正则表达式仅代表 一个 字符不是,或。 我怀疑您要检查字符串是否包含您在正则表达式中描述的这些特殊字符之一。在这种情况下,请将您的正则表达式括起来,以使正则表达式匹配整个字符串。哦,您不必逃脱character class内部。

  • 下面是我在MongoDB的DBCollection对象上调用聚合方法时得到的堆栈跟踪的一部分: 我验证了我的代码实际上是像这样调用该版本的驱动程序: 代码: 产出: 所以,我不确定为什么会引发这个异常,也不知道如何修复它。

  • 问题内容: 为什么此javascript返回108而不是2008?它得到正确的日期和月份,但不正确的年份? year= 108? 问题答案: 这是一个Y2K问题,仅计算自1900年以来的年份。 有迹象表明,目前潜在的兼容性问题已经被弃用,取而代之的-从怪异模式: 为了使事情变得更加复杂,如今不建议使用date.getYear(),而应使用date.getFullYear(),而旧版本的浏览器则不支

  • 问题内容: 而且我不知道为什么! 我基本上有一个STRING(是,不是数组),它具有以下内容: 我想将其转换为String []。因此,首先我从第一个字符和最后一个字符除去substring(),以摆脱方括号[]。然后,我使用split()函数以逗号分隔。我尝试同时使用“ \ |” 和“,”和“ \,”具有相同的结果。 这就是我得到的: 这是它的代码。我把它变成了单线: 如您所见,第一部分是(ma

  • 问题内容: 这是简单的代码,我没有得到设置位图的结果,而是得到了null。谁能告诉我我在哪里犯错了? 更新 好的,所以我无法像我想的那样将文本转换为图像。这样呢 这会创建位图吗? 问题答案: 从文档中: 返回 解码的位图;如果无法解码图像,则 返回 null。 字符串“ test”中涉及的字节不是有效的位图,对吗? 如果将文本“ test”保存在名为or 等的文件中,并试图在Windows中打开它