这些陈述之间有什么区别?

时间:2014-01-15 08:46:12

标签: java numbers ieee-754 floating

这些天,我正在处理有关ACM-ICPC的一些问题(尽管我已经毕业了......只是为了好玩..)。昨天,我几乎变得疯狂,因为有一位在线评委总是对我写的代码说“错误答案”。最后,经过十多个可怕的10个小时后,我意识到以下陈述是原因。

   int target = (int)((double)(M * 100) / N) + 1;          // RIGHT!!
   int target = (int)((double) M / N * 100) + 1;            // WRONG!!

我无法确切地知道第一个语句与第二个语句的行为方式和原因有何不同。因为我不允许看到法官使用的测试用例,所以当代码出错时我很难理解。有没有人可以向我解释一下?谢谢。

*我正在使用Java。

1 个答案:

答案 0 :(得分:2)

据我所知,这两个表达式的结果

(double)(M * 100) / N
(double) M / N * 100
对于浮点精度误差(以及可能的溢出,

是相同的EXCEPT,但是我们在这里忽略它们,因为两条线都容易受到它们的影响,尽管方式不同)。这些错误可能导致值略高于或等于整数,并且略低于同一整数,这将导致

(int)((double)(M * 100) / N)
(int)((double) M / N * 100)

相差一个。通常,在处理浮点数时,如果将除法作为最后一个操作,则有更多机会接近“实际”值。

还有一个考虑因素,这可能会非常棘手:第二行中(double)M/N周围没有括号。这可能为优化器提供了额外的自由度,这可以使结果依赖于优化级别。我不知道这是否会发生在Java中。

至于操作的顺序,我在C中尝试了这个特殊情况(因为这对我来说更快):

int i, j, k;
for (i = 1; i <= 100; i++) {
   j = (int)(((double)i / 100) * 100);
   if (i != j) {
      printf("%d -> %d\n", i, j);
   }
   k = (int)(((double)i * 100) / 100);
   if (i != k) {
      printf("%d ?? %d\n", i, k);
   }
}

我机器上的输出是

29 -> 28
57 -> 56
58 -> 57

以10000000替换100,产生587200条相同类型的线(即错误率为5.872%)