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 :

Convertir double en char* avec une precision full range


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2018
    Messages : 2
    Par défaut Convertir double en char* avec une precision full range
    Bonjour,

    dans le but de m'exercer à travailler avec des doubles, j'ai décidé de refaire la fonction modf.

    Mon modf fonctionne impeccablement quand je travaille avec des petits nombre style 3.25, 15.258, etc.
    Cependant, lorsque je travaille avec des grands nombres, mon résultat diffère du modf original.

    Je vous envoie un screen pour que vous compreniez bien.
    Nom : Screen Shot 2018-11-27 at 13.22.18.png
Affichages : 175
Taille : 52,2 Ko

    Je ne comprends pas comment cela se fait-il.
    Je vous mets le code ci-dessous. Le principe est assez simple, je convertis uniquement la partie entière du nombre en chaine de caractère. Lorsque j'ai cela, je le reconvertis en double afin de ne plus avoir de chiffres après la virgule. Grâce à cela, je possède déjà la partie entière que je pourrais renvoyer. Ensuite pour la partie fractionnaire, je fais "nombre reçu - partie entière".
    Mais je pense qu'il y a quelque chose que je fais mal au moment de la transformation de double a char*. Peut-être n'ai-je pas encore tout compris a la façon dont on peut travailler avec des doubles ?

    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
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
     
    #include <float.h>
    #include <stdio.h>
    #include <ctype.h>
    #include <stdlib.h>
     
    /*
    ** Fonction exposant
    */
    static long double my_exp(double n, int exp)
    { 
    	long double	value_exp;
    	int			i;
     
    	value_exp = n;
    	i = 0;
    	if (exp == 0)
    		return (1.0);
    	while (i < exp - 1)
    	{
    		value_exp *= n;
    		i++;
    	}
    	return (value_exp);
    }
     
    /*
    ** Fonction permettant de compter le nombre de chiffres avant la virgule
    ** afin de creer une bonne taille de tableau avec malloc()
    */
    static int	set_before_comma(double n)
    {
    	int nb_before_comma;
    	double a;
     
    	nb_before_comma = 0;
    	a = 1.0;
    	if (n < a)
    		return (1);
    	while (n >= a && a <= DBL_MAX)
    	{
    		nb_before_comma++;
    		a *= 10.0;
    	}
    	return (nb_before_comma);
    }
     
    /*
    ** Fonction permettant de diviser et stocker dans des doubles la partie entiere et la partie fractionnaire
    ** du nombre "double x"
    */
    static int 		exploder(double x, double *ipart, double *fpart, char *stringx)
    {
    	int i;
     
    	*ipart = 0;
    	i = 0;
    	if (x < 0)
    		x *= -1;
    	if (!isdigit(stringx[0]))
    		i++;
    	while (stringx[i])
    	{
    		*ipart *= 10;
    		*ipart += stringx[i] - 48;
    		i++;
    	}
    	*fpart = x - *ipart;
    	if (stringx[0] == '-')
    	{
    		*ipart *= -1;
    		*fpart *= -1;
    	}
    	return (1);
    }
     
     
    /*
    ** Fonction qui convertit la partie entière du nombre x en chaine de caractère
    */
    static int		intdtoa(double x, int is_neg, double *ipart, double *fpart)
    {
    	int size_array;
    	char *stringx;
    	double nb;
    	int i;
     
    	i = 0;
    	nb = x;
    	if (is_neg)
    		nb *= -1;
    	size_array = set_before_comma(nb);
    	if (!(stringx = (char*)malloc(sizeof(char) * (size_array + is_neg + 1))))
    		return (0);
            stringx[size_array + is_neg] = '\0';
    	if (is_neg)
    		stringx[i++] = '-';
    	nb /= my_exp(10, size_array - 1);
    	while (i < size_array)
    	{
    		stringx[i] = ((int)nb) + 48;
    		nb *= 10;
    		nb -= ((stringx[i] - 48) * 10);
    		i++;
    	}
    	exploder(x, ipart, fpart, stringx);
    	return (1);
    }
     
    double			my_modf(double x, double *intpart)
    {
    	double	fractpart;
    	int 	is_neg;
     
    	is_neg = 0;
    	if (x < 0)
    		is_neg = 1;
    	if (x == DBL_MAX)
    	{
    		*intpart = DBL_MAX;
    		fractpart = 0.000000;
    	}
    	else if (x == -DBL_MAX)
    	{
    		*intpart = -DBL_MAX;
    		fractpart = -0.000000;
    	}
    	else
    	{
    		fractpart = 0;
    		intdtoa(x, is_neg, intpart, &fractpart);
    	}
    	return (fractpart);
    }
    Voila, j'espère avoir été le plus clair, si vous avez des questions, n'hésitez pas !

    Bien à vous,
    Boris

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    Décomposer un flottant est beaucoup plus complexe qu'il n'y parait.
    Toute opération peut faire perdre au 1 bit de précision (addition ou soustraction ou multiplication par 10), ou même 2 bits (division par 10). Dans une boucle on arrive rapidement à une quinzaine de bits perdus.
    En ici tu es dans le cas 'simple' du nombre entier. S'il y a des décimales ça sera pire.

    Le moyen le plus simple est de laisser faire le système. un sprintf est capable de générer toutes les décimales d'un nombre (car il sait gérer correctement cette perte de précision par un système tabulé). Puis de travailler sur la chaîne de caractère produite pour en extraire la partie entière et la partie décimale.
    A ce sujet, on peut utiliser un tableau taille fixée à 20 caractères sans malloc pour ne pas oublier de libérer la mémoire allouée - ça arrive à certains.

  3. #3
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 760
    Par défaut
    Citation Envoyé par dalfab Voir le message
    Puis de travailler sur la chaîne de caractère produite pour en extraire la partie entière et la partie décimale.
    Ouais , passer par des bignums (Arbitrary-precision arithmetic en anglais) ... la chaîne de caractères étant l'implémentation la plus triviale

  4. #4
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2018
    Messages : 2
    Par défaut
    @dalfab
    Salut ! Merci beaucoup pour ta réponse, effectivement les problèmes de précision que je rencontre deviennent bien plus cohérent comme ça!
    Cependant tu m'as un peu mis l'eau à la bouche, j'ai envie d'en savoir plus ! je n'ai pas réussi à trouver des infos quant à la gestion de la perte de précision par un système tabulé. Aurait-eu des références à me donner? Ca m'intéresse énormément !
    Et n'est-il pas possible de compter le nombre de bits perdus et de rectifier le tir sur la valeur finale en y, par exemple, rajoutant ou supprimant quelques bits?

    @foetus
    Merci pour le tips, je vais me renseigner sur les bignums.

  5. #5
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Ça s'appuie directement le format des nombres IEEE 754, l'exposant est limité et la mantisse binaire est limitée. Et 10 s'écrit 101 * 2^1 en binaire ce qui peut faire perdre jusqu'à 3 bits par multiplication.

    Les bits perdus sont perdus, on ne peut pas les réinventer. Et il faut des traitements particuliers pour ne perdre qu'un bit par multiplication. Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     X=20 en binaire :         10100  s'écrit 1.01 * 2^4      utilise 3 bits de mantisse
    multiplié par 10 :      11001000  s'écrit 1.1001 * 2^7    utilise 5 bits de mantisse
    remultiplié par 10 : 11111010000  s'écrit 1.111101 * 2^10 utilise 7 bits de mantisse
    Un double peut normalement gérer 53 bits de mantisses.
    Imaginons ici un 'minidouble' qui ne gère que 6 bits de mantisse, le dernier nombre perd le dernier bit devenant 1.11110 * 2^10 soit 1984 au lieu de 2000.

Discussions similaires

  1. Réponses: 4
    Dernier message: 11/06/2012, 16h11
  2. Réponses: 4
    Dernier message: 25/01/2007, 14h41
  3. Aide sur une selection de chaine de char avec une souris
    Par Baharroth dans le forum AWT/Swing
    Réponses: 2
    Dernier message: 15/11/2006, 21h17
  4. initialisé un char avec une variable
    Par shawty dans le forum C
    Réponses: 8
    Dernier message: 26/06/2006, 20h26
  5. probleme avec une requete full text
    Par maxxou dans le forum Langage SQL
    Réponses: 2
    Dernier message: 07/03/2005, 17h20

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