6

我正在从 Programming in Java book-site 中解决这个问题(为了练习,不是 HW .. Q15 在http://introcs.cs.princeton.edu/java/13flow/):

求谐波级数 1/1 + 1/4 + 1/9 + 1/16 + ... + 1/N2 的总和。for 循环有 4 种变体,其中一些应该给出正确的答案。我的预期答案在评论中,实际结果如下。

public class OneThreeExFifteen {
    public static void main(String[] args) { 
        int N = 1000000;
        double s1=0 , s2 = 0, s3 = 0, s4=0;

        for (int i = 1; i <= N ; i++ )
            s1 = s1 + 1 / ( i * i );        // Expected  s1 = 1 
        for (int i = 1 ; i <= N ; i++ ) 
            s2 = s2 + 1.0 / i * i;          // Expected  s2 = 1000000 
        for (int i = 1 ; i <= N ; i++) 
            s3 = s3 + 1.0 / (i * i) ;       // Correctly computes the series sum 
        for (int i = 1; i <= N ; i++ ) 
            s4 = s4 + 1 / (1.0 * i * i) ;   // Correctly computes the serires sum

        System.out.println("for loop 1" + s1);
        System.out.println("for loop 2" +s2);
        System.out.println("for loop 3" +s3);
        System.out.println("for loop 4" +s4);


    }
}

结果:

for loop 1       (  I get a Divide by 0  error - had to comment out this loop) 
for loop 2   1000000.0 
for loop 3   Infinity 
for loop 4   1.64493306684877

问题 - 为什么我会得到

a) 除以零误差?

b) for 循环 3 的无穷大结果?

4

4 回答 4

5

当然,正如另一个人已经说过的那样,您已经1 / ( i * i )在多个地方使用代码执行了整数除法。在 Java 中, anint除以 anint必须保持 an int,因此1除以更大的数产生0。但是出于这个原因,您不会被零除。这只会让你零,完全不例外。

您正在循环i01000000(100 万)。在你走得太远之前,i65536216 次方)。当这个迭代发生时,i * i溢出(“真正的”结果是2^32)并且你得到0. 此结果导致在第一个循环中除以零。

演示程序:

public static void main (String args[]) throws IOException
{
  int i = 1 << 16;  // 2^16, or 65536
  System.out.println(i);
  int j = i * i;
  System.out.println(j);
}

输出:

65536
0

第三个for循环非常相似,除了浮点除法产生Infinity(合法的浮点值)而不是除以零错误。

第二个和第四个for循环正确地将乘法提升到运算double之前,因此不会发生溢出。但是第二个for循环缺少括号,因此1每次都添加。第四个for循环正确计算总和。

于 2013-11-08T19:41:47.250 回答
3

您在这里遇到整数除法:

s1 = s1 + 1 / ( i * i );

i是一个int,并且每个没有后缀的数字都是隐含的int,所以你要除以 1/1,结果是 1,幸运的是。

但是,当增加时,事情就会发生i- 如果它大于 1,那么在循环结束之前,每个结果都会得到 0。

弄清楚这一点时,我的调试器中发生了一些不寻常的事情-当数字为时发生被零除65536-您在 65536 2上出现溢出-即 2 32-导致 int 值变为 0。

第二个循环:

s2 = s2 + 1.0 / i * i;

你被运算符优先级烧伤了。除法和乘法的优先级高于加法,所以这实际上在做的是:

s2 = s2 + ((1.0 / i) * i);

你划分正确,但你的运算符优先级全错了。

第三个循环:

s3 = s3 + 1.0 / (i * i) ;

这与上面的问题相同,但是由于您处于浮点上下文中,因此除以 0 的任何结果都会产生有符号的Infinity.

于 2013-11-08T19:31:25.117 回答
3

System.out.println(65536*65536);

这将输出 0。

我认为这解释了你除以 0 的问题。你得到一个整数溢出。整数只能容纳到某个点的数字。

但是,正如其他地方所述,整数除法对您的伤害很大。即使您没有除以 0,您的答案也会因为整数除法而出错。

于 2013-11-08T19:42:03.443 回答
0

第一个只是一个溢出错误。证明这一点的一种简单方法是在除法之前显示 i 的值(它将是 65536 或 2^16)。int 的限制是 2^32,整数在超过最大值时会循环,所以 2^32 = 0

(i * i) = (2^16 * 2^16) = 2^32 = 0

于 2013-11-08T19:43:44.763 回答