9

此方法返回“真”。为什么 ?

public static boolean f() {
   double val = Double.MAX_VALUE/10;
   double save = val;
   for (int i = 1; i < 1000; i++) {
       val -= i;
   }
   return (val == save);
}
4

8 回答 8

25

您正在从一个巨大的价值中减去一个很小的价值(小于 1000)。小值比大值小得多,以至于最接近理论结果的可表示值仍然是原始值。

基本上这是浮点数工作方式的结果。

想象一下,我们有一些十进制浮点类型(只是为了简单起见),它只在尾数中存储 5 个有效数字,以及 0 到 1000 范围内的指数。

你的例子就像写 10 999 - 1000 ......想想当四舍五入到 5 个有效数字时结果会是什么。是的,确切的结果是 99999.....9000(有 999 位数字),但如果您只能用 5 个有效数字表示值,那么最接近的结果又是 10 999

于 2013-02-06T17:02:33.220 回答
2

Double.MAX_VALUE如此之大,以至于 JVM 无法区分它和Double.MAX_VALUE-1000

如果从结果中减去小于“1.9958403095347198E292”的数字,Double.MAV_VALUE仍然是Double.MAX_VALUE.

System.out.println(  
            new BigDecimal(Double.MAX_VALUE).equals( new BigDecimal(  
                        Double.MAX_VALUE - 2.E291) )  
                ); 

System.out.println(  
           new BigDecimal(Double.MAX_VALUE).equals( new BigDecimal(  
                        Double.MAX_VALUE - 2.E292) )  
                       ); 

输出:

真的

错误的

于 2013-02-06T17:09:39.887 回答
2

当您设置val为 Double.MAX_VALUE/10 时,它被设置为大约等于 的值1.7976931348623158 * 10^307。从中减去 1000 之类的值将需要双精度表示,这是不可能的,因此它基本上val保持不变。

根据您的需要,您可以使用BigDecimal而不是double.

于 2013-02-06T17:04:34.480 回答
1

Double.MAX_VALUE与 1 或 1000 相比,这是一个巨大的数字。Double.MAX_VALUE-1通常等于Double.MAX_VALUE。所以你的代码在减去 1 或 1000 到Double.MAX_VALUE/10. 永远记住:

  1. doubles 或floats 只是实数的近似值,它们只是有理数,在实数中分布不均
  2. 您应该非常小心地在doubles 或floats 之间使用不相近的算术运算符(还有许多其他规则,例如这样......)
  3. 一般来说,永远不要使用doubles 或者float如果你需要任意精度
于 2013-02-06T17:08:18.550 回答
1

double 没有足够的精度来执行您尝试的计算。所以结果与初始值相同。

==这与运营商无关。

于 2013-02-06T17:02:50.797 回答
1

val是一个很大的数字,当从中减去1(或什1000至 )时,结果不能正确地表示为一个double值。这个数字的表示xx-1是一样的,因为double只有有限的位数来表示无限的数字。

于 2013-02-06T17:04:46.000 回答
0

浮点计算的结果是最接近准确答案的可表示值。这个程序:

public class Test {
  public static void main(String[] args) throws Exception {
    double val = Double.MAX_VALUE/10;
    System.out.println(val);
    System.out.println(Math.nextAfter(val, 0));
  }
}

印刷:

1.7976931348623158E307
1.7976931348623155E307

这些数字中的第一个是您的原始 val。第二个是小于它的最大双倍。

当您从 1.7976931348623158E307 中减去 1000 时,确切答案在这两个数字之间,但非常非常接近 1.7976931348623158E307 而不是 1.7976931348623155E307,因此结果将四舍五入为 1.7976931348623155E3

于 2013-02-07T06:54:12.277 回答
0

因为double浮点数值类型,是数值逼近的一种方式。浮点表示对数字进行编码,以便我们可以存储比通常更大或更小的数字。但是,并非所有数字都可以在给定空间中表示,因此多个数字会四舍五入为相同的浮点值。

作为一个简化的例子,我们可能希望能够在一些通常只能存储 -10 到 10 的小空间中存储范围从 -1000 到 1000 的值。所以我们可以将所有值四舍五入到最接近的千位并将它们存储在小空间中:-1000 编码为-10, -900 编码为-9, 1000 编码为10。但是如果我们想存储 -999 怎么办?我们可以编码的最接近的值是 -1000,因此我们必须将 -999 编码为与 -1000: 相同的值-10

实际上,浮点方案比上面的例子复杂得多,但概念是相似的。数字的浮点表示只能表示所有可能的数字中的一部分,所以当我们有一个不能作为方案的一部分表示的数字时,我们必须将其四舍五入到最接近的可表示值。

在您的代码中,1000 以内的所有值都会Double.MAX_VALUE / 10自动四舍五入为Double.MAX_VALUE / 10,这就是计算机认为(Double.MAX_VALUE / 10) - 1000 == Double.MAX_VALUE / 10.

于 2013-02-06T17:17:13.657 回答