Algo de conversion d'un double vers un char*

7 réponses
Avatar
Gégé
Hello,

Pour des raisons de performance, je suis en train de coder une
alternative =E0 sprintf.
Les r=E9sultats sont plut=F4t bons (x6) par contre je rencontre des
probl=E8mes num=E9riques sur certaines valeurs.

Par exemple :

d =3D 0.00000000001;
double_to_ascii( d, s ); // >> 0.00000000000999

Je suis preneur de toute suggestion on autre piste algorithmique.
Merci !

G



Voici le code


#include <stdio.h>
#include <ctime>
#include <string>
#include <sstream>
#include <iostream>

class timer
{
private:
clock_t t0;
std::string s;

public:
timer( std::string msg )
{
s =3D msg;
t0 =3D clock();
}
~timer()
{
double x =3D ( clock() - t0 ) / ( double ) CLOCKS_PER_SEC;
std::stringstream ss;
ss << x;
std::cout << s << " : " << ss.str() << std::endl;
}
};


void double_to_ascii( double d, char* s )
{
int p =3D 15;

// negative case
if ( d < 0 )
{
*s++ =3D '-';
d =3D -d;
}

// exponential format is dealt with sprintf
const double thres_max =3D (double)(0x7FFFFFFF);
if( d > thres_max )
{
sprintf( s, "%.15G", d );
return;
}

// int / dec parts
int int_d =3D ( int )d;
double dec_d =3D d - int_d;

// int part
char* s_begin =3D s;
do
{
*s++ =3D ( char )( 48 + ( int_d % 10) );
--p;
}
while( int_d /=3D 10 );

// reverse
char* s_end =3D s - 1;
while( s_end > s_begin )
{
char c =3D *s_end;
*s_end-- =3D *s_begin;
*s_begin++ =3D c;
}

// ignore null decimal part
if( dec_d !=3D 0 )
{
// dec separator
*s++ =3D '.';

// dec part
for( int i =3D 0;
i < p;
i++ )
{
double x =3D dec_d * 10;
int_d =3D (int) x;
*s++ =3D ( char )( 48 + int_d );
dec_d =3D x - int_d;
}
}
*s =3D '\0';

}


int main()
{

//------------------ VALUES

double d;
char s[24];

d =3D 0.00000000001;
double_to_ascii( d, s ); /// 0.00000000000999


//------------------ PERFS

const int n =3D 10000000;

{
timer T("double_to_ascii");
for( int i =3D 0; i < n; i++ )
{
double_to_ascii( d, s );
}
}

{
timer T("sprintf");
for( int i =3D 0; i < n; i++ )
{
sprintf( s, "%.15G", d );
}
}

system( "pause" );
return 0;
}

7 réponses

Avatar
Marc
Gégé wrote:

Pour des raisons de performance, je suis en train de coder une
alternative à sprintf.
Les résultats sont plutôt bons (x6) par contre je rencontre des
problèmes numériques sur certaines valeurs.

Par exemple :

d = 0.00000000001;
double_to_ascii( d, s ); // >> 0.00000000000999



Euh, quel est le problème ?
Avatar
Gégé
J'aimerais avoir "0.00000000001"
comme ce que sprintf retournerait
Avatar
Gégé
On 25 nov, 20:37, Marc wrote:
Gégé  wrote:
> Pour des raisons de performance, je suis en train de coder une
> alternative à sprintf.
> Les résultats sont plutôt bons (x6) par contre je rencontre des
> problèmes numériques sur certaines valeurs.

> Par exemple :

>     d = 0.00000000001;
>     double_to_ascii( d, s );  // >> 0.00000000000999

Euh, quel est le problème ?




Un autre exemple plus visuel

d = 0.73;
double_to_ascii( d, s ); /// 0.72999999999999 au lieu de 0.73
Avatar
Fabien LE LEZ
On Fri, 25 Nov 2011 11:02:42 -0800 (PST), Gégé :

Par exemple :

d = 0.00000000001;
double_to_ascii( d, s ); // >> 0.00000000000999



Ça vient probablement du fait que tu ne peux pas stocker la valeur
0.00000000001 dans un double.
Avatar
Frederic Lachasse
Bienvenue dans le monde du calcul en virgule flottante! Et l'un de ses plus gros problème: comme les nombres sont représenté en base 2 et non en base 10, la majorité des nombres à virgule n'a pas de représentation exacte et donc en fait représenté par une approximation. Pour converti r "correctement" (entre guillemet) de la base 2 en base 10, il faut arrondi r...

Je conseille de faire une recherche sur Google avec les mots clefs: "arrond i virgule flottante binaire" et de prévoir une bonne dose d'aspirine.

La citation suivante résume le sujet:
It makes me nervous to fly an airplane since I know they are designed usi ng
floating-point arithmetic.
Anton Householder
Avatar
Gégé
On 26 nov, 03:43, Frederic Lachasse
wrote:
Bienvenue dans le monde du calcul en virgule flottante! Et l'un de ses pl us gros problème: comme les nombres sont représenté en base 2 et non en base 10, la majorité des nombres à virgule n'a pas de représentati on exacte et donc en fait représenté par une approximation. Pour conver tir "correctement" (entre guillemet) de la base 2 en base 10, il faut arron dir...

Je conseille de faire une recherche sur Google avec les mots clefs: "arro ndi virgule flottante binaire" et de prévoir une bonne dose d'aspirine.

La citation suivante résume le sujet:







> It makes me nervous to fly an airplane since I know they are designed u sing
> floating-point arithmetic.
> Anton Householder



Effectivement je sens que je m'embarque dans une sacrée aventure!
Cette page est assez intéressante, je vais partir de ça :
http://code.google.com/p/stringencoders/wiki/NumToA
Avatar
Lucas Levrel
Le 25 novembre 2011, Gégé a écrit :

Pour des raisons de performance, je suis en train de coder une
alternative à sprintf.
Les résultats sont plutôt bons (x6) par contre je rencontre des
problèmes numériques sur certaines valeurs.

Par exemple :

d = 0.00000000001;
double_to_ascii( d, s ); // >> 0.00000000000999

Je suis preneur de toute suggestion on autre piste algorithmique.



Tu devrais calculer une décimale de plus que n'exige la précision, et
arrondir en fonction.

--
LL