5
votes

Qu'est-ce qui ferait décrémenter une boucle for alors qu'elle est censée s'incrémenter?

J'ai écrit une méthode pour calculer depuis combien de temps un père était deux fois plus âgé que son fils et dans combien d'années cela serait vrai. De manière inattendue, il revient "il y a -2 ans" pour un père de 8 ans et un fils de 3 ans. De manière tout aussi inattendue, il revient «dans 1 an» pour un père de 3 ans et un fils de 2 ans. Je ne suis pas préoccupé par la manière d'améliorer le code car je sais déjà comment faire cela. Au lieu de cela, je suis perplexe quant à la raison pour laquelle le compteur de boucle for semble décrémenter alors qu'il est censé s'incrémenter.

Voici mon code.

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);
    }
}

Avec deux foisAsOld (8, 3 ), l'incrément de la boucle for semble s'être inversé pour décompter à partir de 0 au lieu de monter. Avec deux foisAsOld (3, 2), le -1 pourrait représenter une erreur indiquant que le père n'a jamais été deux fois plus âgé que son fils et ne le sera jamais. Ce que je ne comprends pas, c'est ce qui provoquerait une boucle for pour commencer à décrémenter la valeur i lorsqu'elle est censée s'incrémenter. Je m'attendais à ce que le compteur s'incrémente indéfiniment jusqu'à ce que le programme soit à court de mémoire.

Je sais déjà comment améliorer ce programme, mais je suis curieux de savoir comment le compteur d'une boucle for peut diminuer lorsqu'il est censé augmenter. Quelqu'un peut-il expliquer cela?

(MISE À JOUR: Merci à tous pour vos réponses. Je ne peux pas croire que j'ai oublié le débordement d'entier. J'ai essayé de rendre les variables longues au lieu d'entiers, mais cela a rendu le programme encore plus lent. Quoi qu'il en soit, maintenant je me rends compte que le compteur augmentait tout le temps jusqu'à ce qu'il ait survolé et atterri à une valeur négative.


1 commentaires

Puisqu'il n'y a pas de décrément de cette valeur, il s'agit presque certainement d'un débordement d'entier.


3 Réponses :


4
votes

Il est devenu négatif car c'est ce qui se passe en Java lorsqu'un calcul int déborde.

Jetez un œil à https: // docs. oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.2

Il dit que

Si une addition d'entiers déborde, alors le résultat est les bits de poids faible de la somme mathématique représentée dans un format de complément à deux suffisamment grand. En cas de dépassement de capacité, le signe du résultat n'est pas le même que le signe de la somme mathématique des deux valeurs d'opérande.


0 commentaires

1
votes

Lorsque je débogue votre code, je peux voir que yearsAgo s'incrémente sans limite, ce qui fait que pastFathersAge et pastSonsAge deviennent négatifs. Cela provoque un débordement d'entier négatif. Cela se produit parce que votre condition pastFathersAge! = 2 * pastSonsAge n'est jamais remplie (plutôt, jamais PAS remplie). Pas avant que votre futureFathersAge n'ait fait tout son chemin à travers les négatifs, revienne en positif et se soit finalement installé sur -2.

La morale de l'histoire est de s'assurer que la condition de fin de votre boucle peut toujours être remplie. N'utilisez pas ! = , utilisez plutôt > = ou <= .


0 commentaires

3
votes

N'avez-vous pas remarqué que votre programme s'exécute assez lentement? :)

Pour le cas d'il y a (8, 3) ans, votre boucle for continue de boucler et de boucler, essayant de trouver une année où le père est deux fois plus âgé, mais comme nous le savons, le père ne deviendra que deux fois plus vieux dans le futur , mais pas dans le passé. La boucle for ne le sait pas et elle essaiera très fort de trouver une telle année. Il essaie tellement que yearsAgo soit incrémenté au-delà de la valeur maximale de int . Cela provoque un débordement de , et la valeur de yearsAgo "reviendra" à la valeur minimale de int , qui est un nombre négatif. Et puis ce nombre négatif sera incrémenté plusieurs fois, jusqu'à -2.

Il en va de même pour l'autre cas.

Pour résoudre ce problème, vous pouvez ajouter des instructions if pour vérifier si les résultats sont négatifs:

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

Vous pouvez également arrêter la boucle lorsqu'elle atteint des valeurs négatives pour rendre votre programme plus rapide:

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.");
    }

}


0 commentaires