奇怪的双舍入问题

时间:2012-01-05 00:43:54

标签: java floating-point rounding

我遇到了以下愚蠢的函数here

public static String findOutWhatLifeIsAllAbout() {
    int meaning = 0;
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 20; j++) {
        for (int k = 0; k < 300; k++) {
          for (int m = 0; m < 7000; m++) {
            meaning += Math.random() + 1;
          }
        }
      }
    }
    return String.valueOf(meaning).replaceAll("0*$", "");
}

总之,预期结果是字符串“42”,因为Math.random()返回“大于或等于0.0且小于1.0”的双精度数。实际上,在Ubuntu下运行i5时生成的字符串类似于“420000011”,“420000008”! (意思是有时Math.random()的结果是向上舍入

为了掌握哪种双重值会导致Math.random()的结果以某种方式 round 为1,我尝试了这个函数,期待看到一些例子。

public static String findOutWhatLifeIsAllAboutAltered() {
    int meaning = 0;
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 20; j++) {
        for (int k = 0; k < 300; k++) {
          for (int m = 0; m < 7000; m++) {
            double randomResult = Math.random();
            int converted = randomResult;

            if (converted > 0) {
                System.out.println("Misbehaving double = " + randomResult);
            }

            meaning += converted + 1;
          }
        }
      }
    }
    return String.valueOf(meaning).replaceAll("0*$", "");
}

但是这个公式总是返回“42”!任何人都可以提供关于行为在改变的功能中发生变化的原因吗?感谢。

此外,为什么Java让原始函数编译完全? +=电话会不应该出现精度损失错误?

编辑发布了我希望大家看到的代码 - “原始”版本不应该有演员。

4 个答案:

答案 0 :(得分:2)

链接中的代码与最初在此处发布的代码之间存在一个小但重要的区别,

meaning += Math.random() + 1;    // link

VS

meaning += (int)Math.random() + 1;  // StackOverflow

如果此处发布的代码打印出除42之外的任何内容,那么这是一个严重的错误。

此处,Math.random()的结果显式转换为int必须结果为0,然后添加1,结果为1,然后将其添加到meaning

然而,在将int的结果添加到1并将其Math.random()添加到meaning之后,链接帖子中的代码会对meaning = (int)(Math.random() + (double)1 + (double)meaning); 执行隐式转换,基本上

Math.random()

现在,如果double的结果足够接近1.0,则偶尔会发生{{1}}加法的结果向上舍入,因此产生的结果略大于预期的结果。< / p>

答案 1 :(得分:1)

  

+ =呼叫时不应该出现精度损失错误吗?

否。在第一种情况下,您明确地将Math.random()返回的值转换为int。在第二种情况下,meaningconverted1都是整数。


然而,这条线可能存在精度损失:

int converted = randomResult;

http://ideone.com/1ZTDi

答案 2 :(得分:1)

根本不会丢失精度错误,因为在这个例子中它们都是整数 - 你实际上并没有添加任何双精度错误!

你可以将Math.random()的结果转换为int的唯一一行 - 所以你仍然只是在一起添加两个整数。

但是,即使你在int中添加双精度,也仍然不会,因为JLS为这些类型的运算符定义了隐式强制转换:

  

E1 op = E2形式的复合赋值表达式等效于E1 =(T)((E1)op(E2)),其中T是E1的类型,但E1仅被评估一次。

来源: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.26.2

答案 3 :(得分:0)

当您进行类型转换时,精度损失只会出现在这些行上:

double randomResult = Math.random();
int converted = randomResult;

Math.random()返回 0.0 0.0 )和 1.0 独占之间的 double 时,变量已转换只会包含0,因为它已转换为 int

有关双击&gt;的详细信息,请参阅int,见这里:Different answer when converting a Double to an Int - Java vs .Net

我在Mac OS X(Lion)上运行的Java 6系统上尝试了原始程序,它只输出42.您使用的是哪种JRE / JDK?