Algo de conversion d'un double vers un char*
Le
Gégé

Hello,
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.
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 = msg;
t0 = clock();
}
~timer()
{
double x = ( 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 = 15;
// negative case
if ( d < 0 )
{
*s++ = '-';
d = -d;
}
// exponential format is dealt with sprintf
const double thres_max = (double)(0x7FFFFFFF);
if( d > thres_max )
{
sprintf( s, "%.15G", d );
return;
}
// int / dec parts
int int_d = ( int )d;
double dec_d = d - int_d;
// int part
char* s_begin = s;
do
{
*s++ = ( char )( 48 + ( int_d % 10) );
--p;
}
while( int_d /= 10 );
// reverse
char* s_end = s - 1;
while( s_end > s_begin )
{
char c = *s_end;
*s_end-- = *s_begin;
*s_begin++ = c;
}
// ignore null decimal part
if( dec_d != 0 )
{
// dec separator
*s++ = '.';
// dec part
for( int i = 0;
i < p;
i++ )
{
double x = dec_d * 10;
int_d = (int) x;
*s++ = ( char )( 48 + int_d );
dec_d = x - int_d;
}
}
*s = ' ';
}
int main()
{
// VALUES
double d;
char s[24];
d = 0.00000000001;
double_to_ascii( d, s ); /// 0.00000000000999
// PERFS
const int n = 10000000;
{
timer T("double_to_ascii");
for( int i = 0; i < n; i++ )
{
double_to_ascii( d, s );
}
}
{
timer T("sprintf");
for( int i = 0; i < n; i++ )
{
sprintf( s, "%.15G", d );
}
}
system( "pause" );
return 0;
}
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.
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 = msg;
t0 = clock();
}
~timer()
{
double x = ( 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 = 15;
// negative case
if ( d < 0 )
{
*s++ = '-';
d = -d;
}
// exponential format is dealt with sprintf
const double thres_max = (double)(0x7FFFFFFF);
if( d > thres_max )
{
sprintf( s, "%.15G", d );
return;
}
// int / dec parts
int int_d = ( int )d;
double dec_d = d - int_d;
// int part
char* s_begin = s;
do
{
*s++ = ( char )( 48 + ( int_d % 10) );
--p;
}
while( int_d /= 10 );
// reverse
char* s_end = s - 1;
while( s_end > s_begin )
{
char c = *s_end;
*s_end-- = *s_begin;
*s_begin++ = c;
}
// ignore null decimal part
if( dec_d != 0 )
{
// dec separator
*s++ = '.';
// dec part
for( int i = 0;
i < p;
i++ )
{
double x = dec_d * 10;
int_d = (int) x;
*s++ = ( char )( 48 + int_d );
dec_d = x - int_d;
}
}
*s = ' ';
}
int main()
{
// VALUES
double d;
char s[24];
d = 0.00000000001;
double_to_ascii( d, s ); /// 0.00000000000999
// PERFS
const int n = 10000000;
{
timer T("double_to_ascii");
for( int i = 0; i < n; i++ )
{
double_to_ascii( d, s );
}
}
{
timer T("sprintf");
for( int i = 0; i < n; i++ )
{
sprintf( s, "%.15G", d );
}
}
system( "pause" );
return 0;
}
Euh, quel est le problème ?
comme ce que sprintf retournerait
Un autre exemple plus visuel
d = 0.73;
double_to_ascii( d, s ); /// 0.72999999999999 au lieu de 0.73
Ça vient probablement du fait que tu ne peux pas stocker la valeur
0.00000000001 dans un double.
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:
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
Tu devrais calculer une décimale de plus que n'exige la précision, et
arrondir en fonction.
--
LL