IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Imprécision décimale .


Sujet :

C

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    34
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 34
    Points : 17
    Points
    17
    Par défaut Imprécision décimale .
    Bonjour à tous !

    Je suis en train de créer un programme pour trouver des possibilités des briques d'Euler (parallélépipède dont les arrêtes et les diagonales des faces sont des nombres entiers).

    Malheureusement, je suis confronté à un problème, visiblement fréquemment posé sur le forums (mais je n'ai tout de même pas trouvé la solution)

    Voici le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>
     
    #define MAX 100000000000000 //cent mille milliards
    #define STRLEN_MAX 15 //taille (en nombre de caractères) du nombre MAX
     
    int CalculerEtTester(unsigned long long nb1, unsigned long long nb2);
     
    int main()
    {
        //Ouverture d'un fichier servant à enrtegistrer là où le programme s'était arrêté la dernière fois
        FILE* fichier = NULL;
        unsigned long long valeur = 0; //valeur indiquant là où programme s'étati arrêté
        char str[STRLEN_MAX];
     
        fichier = fopen("position.txt", "r");
     
        if(fichier != NULL)
        {
            fgets(str, STRLEN_MAX, fichier);
            valeur = strtoull(str, (char **)NULL, 10); //convertir la valeur en unsigned long long
        }
        else
            valeur = 1;
     
        fclose(fichier);
     
        //trois valeurs correspondant aux arrêtes du parallelépipède
        unsigned long long A;
        unsigned long long B;
        unsigned long long C;
     
        for(A = valeur ; A < MAX ; A++)
        {
            //enregistrement d'où en est de programme dans le fichier
            FILE* fichier = NULL;
            char str[STRLEN_MAX];
     
            fichier = fopen("position.txt", "w+");
            sprintf(str, "%I64u", A);
            fputs(str, fichier);
     
            fclose(fichier);
     
            for(B = A ; B < MAX ; B++)
            {
                if(CalculerEtTester(A, B)) //Si √(A² + B²) est un entier...
                {
                    for(C = B ; C < MAX ; C++)
                    {
                        if(CalculerEtTester(B, C )&& CalculerEtTester(C, A))
                            printf("%I64u - %I64u - %I64u \n", A, B, C);
                    }
                }
            }
        }
        return 0;
    }
     
    int CalculerEtTester(unsigned long long nb1, unsigned long long nb2)
    {
        long double res = fabs(sqrt(pow(nb1, 2) + pow(nb2, 2)));
     
        if (fabs(res - floor(res)) == 0.0f) //si le nombre est enrier (en soustrayant au nombre sa partie entière, on obtient sa partie décimale)
            return 1;
        else
            return 0;
    }
    Voici le premier chiffre que me donne mon programme (après avoir mouliné quelques secondes) :

    1 - 67108864 - 87728297

    Mais, après avoir vérifié les calculs, je me suis rendu compte que sqrt(pow(1, 2) + pow(67108864, 2)) soit √(1² + 67108864²) vaut environ 67108864.000000007450580596923828, ce qui n'est pas une valeur entière...
    Je pense que cela est dû aux long double qui ne sont peut-être pas si précis que cela, mais je ne suis pas sûr, et surtout, je ne sais pas comment résoudre cela.

    Merci de m'aider,

    Syrl

  2. #2
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Bonjour Syrll,

    Effectivement, un long double n'est pas suffisamment précis pour stocker ce genre de valeur.
    Le problème est qu'à part recoder à la main toutes les opérations (sqrt, fabs tout ça) et créer un type ayant potentiellement une "infinité" de chiffres après la virgule, aucun type de base ne peut être utilisé puisque tu auras toujours ce problème de précision.
    Je pense que c'est l'algo qui est à revoir, parce qu'il fait des casts de flottants dans tous les sens (avec pertes de précision à la clé) et que même si il est mathématiquement valide, en pratique il ne couvre pas correctement tous les cas, comme tu as pu le constater.

  3. #3
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2012
    Messages
    62
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2012
    Messages : 62
    Points : 162
    Points
    162
    Par défaut
    Bonjour,
    A titre personnel je ne l'ai jamais utilisé mais de ce que j'ai entendu, tu pourrais trouver ton bonheur avec la bibliothèque GMP. Elle est destinée aux calculs sur des très grand nombres, et aux calculs formels.

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    34
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 34
    Points : 17
    Points
    17
    Par défaut
    Merci beaucoup pour vos réponses
    Oui, j'en étais également arrivé à cette bibliothèque, mais malheureusement, j'ai du mal à l'installer (j'ai toujours galéré à utiliser des bibliothèques...).
    Pouvez-vous m'aider ? Plus précisément, j'aimerais savoir comment indiquer à Code::Blocks (IDE que j'utilise) où se trouve le fichier à inclure.

    J'ai également trouvé une autre solution possible : Savoir si le nombre est un carré parfait sans calculer sa racine. On sait qu'un carré parfait :

    • est multiple de 5 à une unité près ;
    • est multiple de 8, à 0, 1 ou 4 près ;
    • a un résidu (je n'ai pas encore bien saisi ce que c'est) différent de 2, 3, 5, 6 et 8 ;
    • est divisible par 3 et 4 ou alors il l'est si on lui soustrait 1.


    Ou alors, je pourrais trouver les facteurs premiers du nombre, et s'ils se regroupent par 2, c'est un carré parfait...

    Syrl

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    En général, l'égalité est à tester comme "l'écart entre les nombres est assez petit", avec assez petit défini en fonction du type et de la valeur.

    Généralement, on se base sur std::numeric_limits<double> (regarde la ref de <limits>)

    On a une explication du principe dans la faq
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Citation Envoyé par leternel Voir le message
    En général, l'égalité est à tester comme "l'écart entre les nombres est assez petit", avec assez petit défini en fonction du type et de la valeur.
    L'égalité entre nombre flottants.

    M'enfin, ici le problème est posé de telle manière qu'avec uniquement des types builtins c'est difficile...
    Donc vaut mieux faire comme schonai suggérait, utiliser GMP ou alors faire des calculs sur des entiers.
    A voir lequel est le plus rapide des deux.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    34
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 34
    Points : 17
    Points
    17
    Par défaut
    Bon, mon programme avance !
    1. J'ai réussi à installer GMP
    2. J'ai appris réussi à l'utiliser
    3. J'ai trouvé une fonction GENIALE : int mpz_perfect_square (const mpz_t op) : retourne 1 si op est un carré parfait, sinon 0 ==> Plus besoin de l'embêter à utiliser des nombres décimaux !


    Voici alors le code modifié :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>
    #include <gmp.h>
     
    #define MAX 100000000000000 //cent mille milliards
    #define STRLEN_MAX 15 //taille (en nombre de caractères) du nombre MAX
     
    int CalculerEtTester(unsigned long long nb1, unsigned long long nb2);
    int CalculerEtTesterDiagonaleBrique(unsigned long long nb1, unsigned long long nb2, unsigned long long nb3);
     
    int main()
    {
        //Ouverture d'un fichier servant à enrtegistrer là où le programme s'était arrêté la dernière fois
        FILE* fichier = NULL;
        unsigned long long valeur = 0; //valeur indiquant là où programme s'étati arrêté
        char str[STRLEN_MAX]; /*stoque la chaine de caractères correspondant au contenu du fichier*/
     
        fichier = fopen("position.txt", "r");
     
        if(fichier != NULL)
        {
            fgets(str, STRLEN_MAX, fichier);
            valeur = strtoull(str, (char **)NULL, 10); //convertir la valeur en unsigned long long
        }
        else
            valeur = 1;
     
        fclose(fichier);
     
        //trois valeurs correspondant aux arrêtes du parallelépipède
        unsigned long long A;
        unsigned long long B;
        unsigned long long C;
     
        for(A = valeur ; A < MAX ; A++)
        {
            //enregistrement d'où en est de programme dans le fichier
            FILE* fichier = NULL;
            char str[STRLEN_MAX];
     
            fichier = fopen("position.txt", "w+");
            sprintf(str, "%I64u", A);
            fputs(str, fichier);
     
            fclose(fichier);
     
            for(B = A ; B < MAX ; B++)
            {
                if(CalculerEtTester(A, B)) //Si √(A² + B²) est un entier...
                {
                    for(C = B ; C < MAX ; C++)
                    {
                        if(CalculerEtTester(B, C )&& CalculerEtTester(C, A) && CalculerEtTesterDiagonaleBrique(A, B, C))
                            printf("%I64u - %I64u - %I64u \n", A, B, C);
                    }
                }
            }
        }
        return 0;
    }
     
    int CalculerEtTester(unsigned long long nb1, unsigned long long nb2)
    {
        mpz_t puiss1;
        mpz_t puiss2;
        mpz_t res;
     
        mpz_init(puiss1);
        mpz_init(puiss2);
        mpz_init(res);
     
        mpz_ui_pow_ui(puiss1, nb1, 2);
        mpz_ui_pow_ui(puiss2, nb2, 2);
     
        mpz_add(res, puiss1, puiss2);
     
        //gmp_printf("%Zd\n", res);
     
        int boolean = mpz_perfect_square_p(res);
     
        if (boolean)
            return 1;
        else
            return 0;
    }
     
    int CalculerEtTesterDiagonaleBrique(unsigned long long nb1, unsigned long long nb2, unsigned long long nb3)
    {
        mpz_t puiss1;
        mpz_t puiss2;
        mpz_t puiss3;
        mpz_t res;
     
        mpz_init(puiss1);
        mpz_init(puiss2);
        mpz_init(puiss3);
        mpz_init(res);
     
        mpz_ui_pow_ui(puiss1, nb1, 2);
        mpz_ui_pow_ui(puiss2, nb2, 2);
        mpz_ui_pow_ui(puiss3, nb3, 2);
     
        mpz_add(res, puiss1, puiss2);
        mpz_add(res, res, puiss3);
     
        int boolean = mpz_perfect_square_p(res);
     
        if (boolean)
            return 1;
        else
            return 0;
    }
    Malheureusement, après s'être exécuté environ 30 secondes, mon programme s'arrête après avoir affiché le message suivant :
    GNU MP: Cannot reallocate memory (old_size=4 new_size=12)

    This application has requested the Runtime to terminate it in an unusual way.

    Please contact the application's support team for more information.
    Pouvez-vous m'aider ?
    Merci,
    Syrl

  8. #8
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2012
    Messages
    62
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2012
    Messages : 62
    Points : 162
    Points
    162
    Par défaut
    Bonjour,
    Au pif je dirais out of memory . L'impossibilité de ré-allocation suggère que GMP fonctionne avec des allocations dynamique en interne. Ce qui est relativement évident étant donné que la taille des nombres peut grandement varier. Tu utilises énormément de mpz_t avec tes boucles for. Tu utilises une fonction init mais n'y a t'il pas une fonction free/release/destroy/kaboom... pour libérer tes variables ? Si elle existe cette fonction pourrait résoudre ton problème.

    Hors sujet mais ça me fait vraiment trop bauger ^^
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int boolean = mpz_perfect_square_p(res);
     
        if (boolean)
            return 1;
        else
            return 0;
    L=>return mpz_perfect_square_p(res);

  9. #9
    Membre éclairé
    Inscrit en
    Juillet 2012
    Messages
    231
    Détails du profil
    Informations forums :
    Inscription : Juillet 2012
    Messages : 231
    Points : 870
    Points
    870
    Par défaut
    Citation Envoyé par schonai Voir le message
    Tu utilises une fonction init mais n'y a t'il pas une fonction free/release/destroy/kaboom... pour libérer tes variables ?
    Il y en a une effectivement (mpz_clear en l'occurrence) et il faudrait voir à l'utiliser en effet.

  10. #10
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2012
    Messages
    62
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2012
    Messages : 62
    Points : 162
    Points
    162
    Par défaut
    clear !! Ils ont choisi le seul auquel je n'avais pas pensé. C'est un complot d'emacsien ça encore !

  11. #11
    Membre à l'essai
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    34
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 34
    Points : 17
    Points
    17
    Par défaut
    Bien vu !!
    En effet, je n'ai pas libéré la mémoire...
    J'ai alors trouvé la fonction adéquate : mpz_clear.
    Merci beaucoup !
    Du coup, je n'ai pas pu mettre en place le
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return mpz_perfect_square_p(res);
    car sinon je ne pouvais pas libérer le res... Et ça plantait toujours.
    Encore merci !
    Syrl


+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 12
    Dernier message: 29/01/2024, 15h32
  2. zéro décimal
    Par galendor dans le forum Bases de données
    Réponses: 9
    Dernier message: 24/03/2004, 16h18
  3. [DecimalFormat]longueur de partie décimale
    Par Maximil ian dans le forum API standards et tierces
    Réponses: 2
    Dernier message: 04/03/2004, 14h26
  4. Requete avec des décimales
    Par Sandrine75 dans le forum MS SQL Server
    Réponses: 5
    Dernier message: 27/06/2003, 10h18
  5. Réponses: 1
    Dernier message: 06/03/2003, 11h57

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo