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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么会导致for循环在它应该递增时递减?相关的知识,希望对你有一定的参考价值。

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

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

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

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

答案

它变得消极,因为当int计算溢出时,这就是Java中发生的事情。

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

它说

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

另一答案

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

对于(8,3)年前的情况,你的for循环保持循环和循环,试图找到父亲两倍的年份,但据我们所知,父亲将来只会变成两倍,但是不是过去。 for循环不知道这个,它会很难找到这样的一年。它努力使yearsAgo增加超过int的最大值。这导致overflowyearsAgo的值将“回绕”到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++) {
另一答案

当我调试你的代码时,我可以看到yearsAgo正在无限制地递增,导致pastFathersAgepastSonsAge进入底片。这会导致负整数溢出。发生这种情况是因为你的条件pastFathersAge != 2 * pastSonsAge永远不会被满足(相反,永远不会满足)。直到你的futureFathersAge一直走过负面,回到正面,然后最终落在-2。

这个故事的寓意是要确保你的循环终止条件总能得到满足。不要使用!=,而是使用>=<=

以上是关于什么会导致for循环在它应该递增时递减?的主要内容,如果未能解决你的问题,请参考以下文章

对于循环:变量的递增和递减[关闭]

VBA for Access - Do While 循环停止递减

LeetCode210509非递减数列和最大连续递增数列

为啥我的代码会导致 java 中的堆栈溢出错误?它最终应该终止。 for循环版本不会导致错误

i++与++i 递增递减运算符的前置与后置

JS性能优化