Partage
  • Partager sur Facebook
  • Partager sur Twitter

Comparaison erronee

Sujet résolu
    18 mai 2022 à 1:25:08

    Bonsoir, voici un petit programme que j'ai mais qui ne marche pas:
    #include <stdio.h>
    
    int main()
    {
    	double number;
    	scanf("%lf", &number);
    	
    	double n = 0;
    	do{
    		n += 0.001; // EDIT:  n += 1.0e-7
    	} while(n * n < number);
    	printf("Resultat: %.3f\n", n);
    	return 0;
    }

    Si je remplace n += 0.001 par n += 1 y'aura pas de souci. Mais tel que c'est actuellement, la comparaison marche pas. n * n reste inferieur a number d'apres le compilateur (je tombe dans une boucle infinie).

    Je suis assez surpris. Des idees sur la cause de ce probleme s'il vous plait?

    -
    Edité par Asmitta 18 mai 2022 à 8:27:28

    • Partager sur Facebook
    • Partager sur Twitter
      18 mai 2022 à 1:43:31

      Hello,

      Pas de boucle infinie chez moi. Mais quelle valeur donnes-tu comme réponse au scanf() ?  123456 me donne 351.364 immédiatement comme réponse.

      -
      Edité par edgarjacobs 18 mai 2022 à 1:47:16

      • Partager sur Facebook
      • Partager sur Twitter

      On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

        18 mai 2022 à 2:16:16

        Quand on dépasse les 10 chiffres, ça commence à être assez long. J'ai essayé 12345678901234
        Et c'est immédiat avec les nombres négatifs.

        Si c'était pour calculer la racine carrée, on peut faire mieux (en dehors de sqrt() ...)
        Le code suivant ne marche pas pour 1 milliard mais marche pour 900 millions.
        Quelque chose de louche avec scanf ?
        -
        #include <stdio.h>
        #include <math.h>
        int main(void){
            double number;
            scanf("%lf", &number);
            // Approximation grossière de la racine carrée.
            double root = (number > 1) ? number / 10.0 : number *10.0;
            // Je ne teste pas zéro ou les nombres négatifs.
            do {
                root = (number/root + root) /2;
            } while(fabs(number - root*root) > 1.0e-15);
            printf("%lf", root);
        }

        -
        Edité par PierrotLeFou 18 mai 2022 à 3:04:35

        • Partager sur Facebook
        • Partager sur Twitter

        Le Tout est souvent plus grand que la somme de ses parties.

          18 mai 2022 à 3:15:35

          Merci j'ai du envoyer en modifiant quelque chose car en effet ca marche maintenant.

          Sinon quand j'entre 4 j'ai 2.001, pour des grandes valeurs (sur mon code du dessus) le resultat est correct mais pas tres bon pour les petites valeus( < 25).

          Mon precedent code etait:

          	do
          	{
          		if (n * n == number)
          		{
          			printf("Resulat: %.3f", n);
          			break;
          		}
          		n += 0.001;
          	} while (n * n != number);

          Ceci me donne une boucle infinie. Et oui c'est pour la racine caree...

          PierrotLeFou a écrit:

          Quand on dépasse les 10 chiffres, ça commence à être assez long. J'ai essayé 12345678901234
          Et c'est immédiat avec les nombres négatifs.

          Si c'était pour calculer la racine carrée, on peut faire mieux (en dehors de sqrt() ...)
          Le code suivant ne marche pas pour 1 milliard mais marche pour 900 millions.
          Quelque chose de louche avec scanf ?
          -
          #include <stdio.h>
          #include <math.h>
          int main(void){
              double number;
              scanf("%lf", &number);
              // Approximation grossière de la racine carrée.
              double root = (number > 1) ? number / 10.0 : number *10.0;
              // Je ne teste pas zéro ou les nombres négatifs.
              do {
                  root = (number/root + root) /2;
              } while(fabs(number - root*root) > 1.0e-15);
              printf("%lf", root);
          }

          -
          Edité par PierrotLeFou il y a 11 minutes


          Je commence a croire que tu as un code pour tous les calculs....Merci mais pourquoi ces approximations, ou alors comment tu arrives a cela plutot car c'est juste.

          -
          Edité par Asmitta 18 mai 2022 à 3:24:34

          • Partager sur Facebook
          • Partager sur Twitter
            18 mai 2022 à 3:33:43

            D'abord, ce n'est pas souhaité de comparer deux doubles issus de calculs car on n'est pas certain qu'ils sont égaux.
            J'ai corrigé mon code pour qu'il marche tout le temps (j'espère).
            C'est la formule utilisée par sqrt(). Je pense que c'est Newton qui a trouvé la méthode.

            C'est une fonction à convergeance quadratique. Si on a une bonne approximation au départ, ça converge en 5 à 6 itérations.
            -
            #include <stdio.h>
            #include <math.h>
            int main(void){
                double number;
                scanf("%lf", &number);
                // Approximation grossière de la racine carrée.
                double root = (number > 1) ? number / 10.0 : number *10.0;
                // Je ne teste pas zéro ou les nombres négatifs.
                double root1;
                do {
            root1 = root;
                    root = (number/root1 + root1) /2;
                } while(fabs(root1-root) > 1.0e-15);
                printf("%lf", root);
            }

            -
            Edité par PierrotLeFou 18 mai 2022 à 3:42:12

            • Partager sur Facebook
            • Partager sur Twitter

            Le Tout est souvent plus grand que la somme de ses parties.

              18 mai 2022 à 8:26:12

              Comme d'habitude ton code est bien plus rapide que le mien. Le precedent que avais fait marchait pas avec certains nombres comme 2000 mais cette fois c'est okay.

              Merci.

              >D'abord, ce n'est pas souhaité de comparer deux doubles issus de calculs car on n'est pas certain qu'ils sont égaux.

              Je ne savais pas ceci.

              >C'est une fonction à convergeance quadratique. Si on a une bonne approximation au départ, ça converge en 5 à 6 itérations.

              C'est pas mon niveau ici, je pourrais pas expliquer le pourquoi du comment donc je restes avec ma fonction(bien que lente pour de tres grands nombres) mais retenant ta part.

              • Partager sur Facebook
              • Partager sur Twitter
                18 mai 2022 à 11:30:05

                J'écris ici ce que j'ai compris (sous réserve, donc, que j'aie bien compris...)

                Les nombres à virgule sont gérés en base 2.

                Exemple : 0,4 ça vaut combien en base 2 ? Ça vaut 0,011001100110011... Ce n'est pas un nombre rond, il y a une infinité de décimales !

                Imaginons que les nombres soient stockés (en base 2) avec seulement 6 décimales : il stockerait 0,011001. Or, en base 10, ce nombre est égal à 0,390625. (J'ai fait les conversions sur ce site trouvé au hasard : https://www.123calculus.com/conversion-base-n-page-1-11-180.html )

                S'il stocke (en base 2) avec 12 décimales, il stockera 0,011001100110, qui vaut 0,3984375 en base 10.

                Conclusion : quand on manipule des nombres à virgule, on manipule des valeurs approchées. Il ne faut donc pas faire de comparaison du type

                 do ... while (n * n != number);

                Tu devrais faire afficher 'n' avec un grand nombre de décimales pour voir ce qui se passe. Il se passe sans doute ce genre de chose :

                - On croit que n vaut 2,4 mais en réalité il vaut 2,4000000002375.

                - On ajoute 0,1 et on croit que n vaut 2,5 (mettons qu'on cherche la racine carrée de 6,25) sauf qu'il vaut 2,5000000003125.

                - Du coup la boucle continue et ne s'arrêtera jamais.

                Moralité : quand on veut savoir ce qui se passe, il faut afficher les valeurs des variables (ou utiliser un débogueur avec des points d'arrêt).

                Ce qu'il faut faire : un test de valeur approchée.

                #define EPSILON 1.0e-09
                do ... while (fabs(n * n - number) >= EPSILON);  // égalité approchée au milliardième

                -
                Edité par robun 18 mai 2022 à 13:02:30

                • Partager sur Facebook
                • Partager sur Twitter
                  18 mai 2022 à 13:54:33

                  Merci.

                  Le type double est bien plus complexe qu'il le laisse croire...

                  • Partager sur Facebook
                  • Partager sur Twitter
                    18 mai 2022 à 15:20:28

                    Autre chose, on serait tenté de croire qu'après un certain temps:
                    number / root == root
                    C'est vrai dans certains cas, mais il arrive que cela oscille entre deux nombres très proches et on se retrouve dans une boucle infinie.
                    Les valeurs sont aussi précises que la machine le permet.

                    edit:

                    J'ai fait le petit test suivant. Ça devrait marcher sur toute machine respectant le standard IEEE pour les flottants 64-bits.
                    Le nombre 10 milliards est proche de 2^34. Ma boucle de la fin fera 17 itérations mais donne un résultat plus précis.
                    L'idée du début est qu'un nombre flottant a un exposant et une mantisse. Je ne fais que diviser l'exposant par 2 sans toucher la mantisse.
                    Toutefois, je reste dans le même ordre de grandeur que la racine, et la convergeance devrait être aussi rapide que le second résultat.
                    -
                    #include <stdio.h>
                    typedef struct {
                        union {
                            double d;
                            long long n;
                        };
                    } Double;
                    int main(void)  {
                        double number;
                        puts("Entrez un nombre ");
                        scanf("%lf", &number);
                        Double work;
                        work.d = number;
                        work.n -= (((work.n>>52)%2048-1024)/2)<<52;
                        printf("racine %lf\n", work.d);
                        double root = 1.0;
                        double factor = 2.0;
                        do {
                            number /= factor;
                            root *= factor;
                        } while(root < number);
                        root = (number+root) / 2;
                        printf("%lf\n", root);
                    }
                    -
                    Entrez un nombre                                                                                                        
                    10000000000                                                                                                             
                    racine 152587.890625                                                                                                    
                    103682.972656                                                                                                           

                    -
                    Edité par PierrotLeFou 18 mai 2022 à 17:49:21

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Le Tout est souvent plus grand que la somme de ses parties.

                    Comparaison erronee

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown