Что может привести к уменьшению цикла for, когда он должен увеличиваться? - PullRequest
0 голосов
/ 03 января 2019

Я написал метод, чтобы рассчитать, сколько лет тому назад отцу было в два раза больше, чем его сыну, и через сколько лет это будет правдой.Неожиданно он возвращается «-2 года назад» для 8-летнего отца и 3-летнего сына.Не менее неожиданно он возвращает «-1 год спустя» для 3-летнего отца и 2-летнего сына.Меня не волнует, как улучшить код, потому что я уже знаю, как это сделать.Вместо этого я озадачен тем, почему счетчик цикла 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),Инкремент цикла, по-видимому, перевернулся, чтобы начать обратный отсчет от 0 вместо повышения.С дважды AsOld (3, 2), -1 может означать ошибку, указывающую, что отец никогда не был в два раза старше своего сына и никогда не будет.Что я не понимаю, так это то, что заставило бы цикл for начать уменьшать значение i, когда оно должно увеличиваться.Я ожидал, что счетчик будет увеличиваться до бесконечности, пока программе не хватит памяти.

Я уже знаю, как улучшить эту программу, но мне любопытно, как может уменьшиться счетчик в цикле for, когда он должен увеличиться.Кто-нибудь может объяснить это?

(ОБНОВЛЕНИЕ: Спасибо всем за ваши ответы. Не могу поверить, что я забыл о целочисленном переполнении. Я пытался сделать переменные long вместо целых чисел, но это сделало программу еще медленнее. В любом случаеТеперь я понимаю, что счетчик все время увеличивался, пока не пролетел и не приземлился с отрицательным значением.)

Ответы [ 3 ]

0 голосов
/ 03 января 2019

Когда я отлаживаю ваш код, я вижу, что yearsAgo увеличивается без ограничения, в результате чего pastFathersAge и pastSonsAge переходят в негативы.Это вызывает отрицательное целочисленное переполнение.Это происходит потому, что ваше условие pastFathersAge != 2 * pastSonsAge никогда не выполняется (точнее, никогда НЕ выполняется).До тех пор, пока ваш futureFathersAge не пройдёт весь путь через негативы обратно в позитивы и, наконец, не остановится на -2.

Мораль этой истории состоит в том, чтобы убедиться, что ваше условие завершения для вашего цикла всегда можетможно встретить.Не используйте !=, используйте >= или <=.

0 голосов
/ 03 января 2019

Разве вы не заметили, что ваша программа работает довольно медленно?:)

В случае (8, 3) лет назад ваш цикл for продолжает циклически повторяться, пытаясь найти год, в котором отец в два раза старше, но, как мы знаем, отец станет тольков два раза старше в будущем , но не в прошлом.Цикл for этого не знает, и будет очень стараться найти такой год.Он пытается настолько , что yearsAgo увеличивается до максимального значения int.Это вызывает переполнение , и значение 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++) {
0 голосов
/ 03 января 2019

Это стало отрицательным, потому что это то, что происходит в Java, когда вычисление int переполняется.

Взгляните на https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.2

Это говорит о том, что

Если целочисленное сложение переполняется, то результатом являются младшие биты математической суммыкак представлено в некотором достаточно большом формате с двумя дополнениями.Если происходит переполнение, то знак результата не совпадает со знаком математической суммы двух значений операнда.

...