什么会导致 for 循环在应该递增时递减?

What would cause a for loop to decrement when it's supposed to increment?

我写了一个方法来计算父亲的年龄是他儿子的两倍,以及从现在起多少年后这将是真实的。没想到,对于一个8岁的父亲和一个3岁的儿子来说,returns“-2年前”。同样出乎意料的是,对于一个 3 岁的父亲和一个 2 岁的儿子来说,它 returns “-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);
    }
}

对于 twiceAsOld(8, 3),for 循环的增量似乎已反转为从 0 而不是向上计数。对于 twiceAsOld(3, 2),-1 可能代表一个错误,表明父亲的年龄从未是他儿子的两倍,而且永远不会。我不明白的是什么会导致 for 循环在应该递增时开始递减 i 值。我原以为计数器会无限递增,直到程序 运行 内存不足。

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

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

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

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

它说

If an integer addition overflows, then the result is the low-order bits of the mathematical sum as represented in some sufficiently large two's-complement format. If overflow occurs, then the sign of the result is not the same as the sign of the mathematical sum of the two operand values.

当我调试您的代码时,我可以看到 yearsAgo 正在无限递增,导致 pastFathersAgepastSonsAge 变为负值。这导致负整数溢出。发生这种情况是因为您的条件 pastFathersAge != 2 * pastSonsAge 从未满足(更确切地说,从未满足)。直到您的 futureFathersAge 一路经历负数,回到正数,并最终落在 -2 上。

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

你没注意到你的程序运行很慢吗? :)

对于(8, 3)年前的情况,你的for循环一直在循环,试图找到父亲是两倍大的年份,但据我们所知,父亲只会变成两倍大将来,但过去不会。 for 循环不知道这一点,它会非常努力地寻找这样的年份。它尝试 so 使 yearsAgo 递增超过 int 的最大值。这就造成了一个overflowyearsAgo的值会"wrap back around"到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++) {