【问题标题】:What would cause a for loop to decrement when it's supposed to increment?什么会导致 for 循环在应该递增时递减?
【发布时间】:2019-05-29 15:15:43
【问题描述】:

我写了一个方法来计算一个父亲在多久以前是他儿子的两倍,从现在开始多少年后这是真的。没想到,它为一个 8 岁的父亲和一个 3 岁的儿子返回“-2 年前”。同样出乎意料的是,它为 3 岁的父亲和 2 岁的儿子返回“-1 年后”。我不关心如何改进代码,因为我已经知道如何做到这一点。相反,我很困惑为什么 for 循环计数器在应该递增时却出现递减。

这是我的代码。

public class TwiceAsOld {

    public static void twiceAsOld (int currentFathersAge, int currentSonsAge) {

        int yearsAgo;
        int yearsFromNow;
        int pastFathersAge = currentFathersAge;
        int pastSonsAge = currentSonsAge;
        int futureFathersAge = currentFathersAge;
        int futureSonsAge = currentSonsAge;

        for (yearsAgo = 0; pastFathersAge != 2 * pastSonsAge; yearsAgo++) {
            pastFathersAge--;
            pastSonsAge--;
        }

        System.out.println("The father was last twice as old as the son " + yearsAgo + " years ago.");

        for (yearsFromNow = 0; futureFathersAge != 2 * futureSonsAge; yearsFromNow++) {
            futureFathersAge++;
            futureSonsAge++;
        }

        System.out.println("The father will be twice as old as the son in " + yearsFromNow + " years from now.");

    }

    public static void main(String[] args) {
        twiceAsOld(8, 3);
        twiceAsOld(3, 2);
    }
}

使用 twoAsOld(8, 3) 时,for 循环的增量似乎已反转为从 0 倒计时而不是向上倒计时。对于 twoAsOld(3, 2),-1 可能代表一个错误,表明父亲从来没有比儿子大两倍,也永远不会。我不明白的是什么会导致 for 循环在 i 值应该增加时开始减少它。我期待计数器无限增加,直到程序内存不足。

我已经知道如何改进这个程序,但我很好奇 for 循环中的计数器在应该增加时如何减少。谁能解释一下?

(更新:感谢大家的回答。我不敢相信我忘记了整数溢出。我尝试将变量设为 long 而不是整数,但这使程序变得更慢。无论如何,现在我意识到计数器是一直递增,直到它溢出并降落在负值。)

【问题讨论】:

  • 由于该值没有递减,几乎可以肯定是整数溢出。

标签: java loops for-loop increment decrement


【解决方案1】:

它变成了负数,因为这是在 Java 中 int 计算溢出时发生的情况。

看看 https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.2

上面写着

如果整数加法溢出,则结果是数学和的低位,以某种足够大的二进制补码格式表示。如果发生溢出,则结果的符号与两个操作数值的数学和的符号不同。

【讨论】:

    【解决方案2】:

    您没有注意到您的程序运行得很慢吗? :)

    对于 (8, 3) 年前的情况,你的 for 循环不断循环,试图找到父亲年龄翻倍的年份,但正如我们所知,父亲只会变老两倍 在未来,但不是在过去。 for 循环不知道这一点,它会非常努力地找到这样的一年。它尝试如此使yearsAgo 增加到超过int 的最大值。这会导致overflow,并且yearsAgo 的值将“回绕”到最小值int,这是一个负数。然后这个负数会增加很多次,直到-2。

    其他情况也是如此。

    要解决这个问题,您可以添加 if 语句来检查结果是否为负:

    public static void twiceAsOld (int currentFathersAge, int currentSonsAge) {
    
        int yearsAgo;
        int yearsFromNow;
        int pastFathersAge = currentFathersAge;
        int pastSonsAge = currentSonsAge;
        int futureFathersAge = currentFathersAge;
        int futureSonsAge = currentSonsAge;
    
    
        for (yearsAgo = 0; pastFathersAge != 2 * pastSonsAge; yearsAgo++) {
    
            pastFathersAge--;
            pastSonsAge--;
        }
    
        // Here!
        if (yearsAgo >= 0) {
            System.out.println("The father was last twice as old as the son " + yearsAgo + " years ago.");
        }
    
        for (yearsFromNow = 0; futureFathersAge != 2 * futureSonsAge; yearsFromNow++) {
            futureFathersAge++;
            futureSonsAge++;
        }
    
        if (yearsFromNow >= 0) {
            System.out.println("The father will be twice as old as the son in " + yearsFromNow + " years from now.");
        }
    
    }
    

    您也可以在循环达到负值时停止循环以使您的程序更快:

    for (yearsAgo = 0; pastFathersAge != 2 * pastSonsAge && yearsAgo >= 0; yearsAgo++) {
    

    【讨论】:

      【解决方案3】:

      当我调试您的代码时,我可以看到yearsAgo 无限制地递增,导致pastFathersAgepastSonsAge 变为负数。这导致负整数溢出。发生这种情况是因为您的条件 pastFathersAge != 2 * pastSonsAge 从未满足(而是从未满足)。直到您的futureFathersAge 一直处于负面状态,回到正面,最终确定为 -2。

      故事的寓意是确保您的循环的终止条件始终可以满足。不要使用!=,而是使用>=<=

      【讨论】:

        猜你喜欢
        • 2014-02-03
        • 2011-10-15
        • 1970-01-01
        • 2020-03-06
        • 2021-07-01
        • 2013-05-04
        • 2020-11-26
        • 2014-09-10
        • 1970-01-01
        相关资源
        最近更新 更多