【问题标题】:for loop debug in java - value overflowjava中的for循环调试-值溢出
【发布时间】:2013-11-08 19:29:38
【问题描述】:

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

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

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 的结果是否为无穷大?

【问题讨论】:

    标签: java debugging for-loop


    【解决方案1】:

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

    您正在从0 循环到i1000000(100 万)。在你走得太远之前,i655362 的 16 次方)。当这个迭代发生时,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 循环正确计算总和。

    【讨论】:

    • 知道 (2^16)^2 = 2^32 = 0 的数学有助于我猜。但是计算机足够强大,我刚刚做了for(int i = 0 ; i &lt; 1000000 ; i++)if(i*i==0)System.out.println(i); :)
    【解决方案2】:

    System.out.println(65536*65536);

    这将输出 0。

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

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

    【讨论】:

      【解决方案3】:

      您在这里遇到整数除法:

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

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

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

      在解决这个问题时,我的调试器中发生了一些不寻常的事情 - 当数字为 65536 时发生被零除 - 你在 655362 上出现溢出 - 这是 2 32 - 导致 int 值变为 0。

      第二个循环:

      s2 = s2 + 1.0 / i * i;
      

      您正在被运算符优先级所困扰。除法和乘法的优先级高于加法,所以这实际上是在做:

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

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

      第三个循环:

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

      这与上面的问题相同,但由于您处于浮点上下文中,anything divided by 0 results in a signed Infinity.

      【讨论】:

      • 恰如其分。我知道第一个循环不会给我正确的答案,但它不应该看到除以 0 错误
      • @Nik 不,你绝对应该。使用 32 位有符号整数 65536*65536 = 0
      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-25
        • 1970-01-01
        • 2016-09-03
        • 2023-03-07
        相关资源
        最近更新 更多