panthema / 2006 / SDIOS06 / sdios06 / lib / libc / __dtostr.c (Download File)
#include <stdlib.h>
#include <math.h>

/* convert double to string.  Helper for sprintf. */

static int copystring(char* buf,int maxlen, const char* s) {
	int i;
	for (i=0; i<3&&i<maxlen; ++i)
		buf[i]=s[i];
	if (i<maxlen) { buf[i]=0; ++i; }
	return i;
}

int __dtostr(double d,char *buf,unsigned int maxlen,unsigned int prec,unsigned int prec2) {
#if 1
	union {
		unsigned long long l;
		double d;
	} u = { .d=d };
	/* step 1: extract sign, mantissa and exponent */
	signed long e=((u.l>>52)&((1<<11)-1))-1023;
#else
	signed long e=(((((unsigned long*)&d)[1])>>20)&((1<<11)-1))-1023;
#endif
/*  unsigned long long m=u.l & ((1ull<<52)-1); */
	/* step 2: exponent is base 2, compute exponent for base 10 */
	signed long e10;
	/* step 3: calculate 10^e10 */
	unsigned int i;
	double backup=d;
	double tmp;
	char *oldbuf=buf;

	if ((i=isinf(d))) return copystring(buf,maxlen,i>0?"inf":"-inf");
	if (isnan(d)) return copystring(buf,maxlen,"nan");
	e10=1+(long)(e*0.30102999566398119802); /* log10(2) */
	/* Wir iterieren von Links bis wir bei 0 sind oder maxlen erreicht
	 * ist.  Wenn maxlen erreicht ist, machen wir das nochmal in
	 * scientific notation.  Wenn dann von prec noch was übrig ist, geben
	 * wir einen Dezimalpunkt aus und geben prec2 Nachkommastellen aus.
	 * Wenn prec2 Null ist, geben wir so viel Stellen aus, wie von prec
	 * noch übrig ist. */
	if (d==0.0) {
		prec2=prec2==0?1:prec2+2;
		prec2=prec2>maxlen?8:prec2;
		i=0;
		if (prec2 && (long long)u.l<0) { buf[0]='-'; ++i; }
		for (; i<prec2; ++i) buf[i]='0';
		buf[buf[0]=='0'?1:2]='.'; buf[i]=0;
		return i;
	}

	if (d < 0.0) { d=-d; *buf='-'; --maxlen; ++buf; }

	/*
      Perform rounding. It needs to be done before we generate any
      digits as the carry could propagate through the whole number.
	*/

	tmp = 0.5;
	for (i = 0; i < prec2; i++) { tmp *= 0.1; }
	d += tmp;

	if (d < 1.0) { *buf='0'; --maxlen; ++buf; }
/*  printf("e=%d e10=%d prec=%d\n",e,e10,prec); */
	if (e10>0) {
		int first=1;	/* are we about to write the first digit? */
		tmp = 10.0;
		i=e10;
		while (i>10) { tmp=tmp*1e10; i-=10; }
		while (i>1) { tmp=tmp*10; --i; }
		/* the number is greater than 1. Iterate through digits before the
		 * decimal point until we reach the decimal point or maxlen is
		 * reached (in which case we switch to scientific notation). */
		while (tmp>0.9) {
			char digit;
			double fraction=d/tmp;
			digit=(int)(fraction);		/* floor() */
			if (!first || digit) {
				first=0;
				*buf=digit+'0'; ++buf;
				if (!maxlen) {
					/* use scientific notation */
					int len=__dtostr(backup/tmp,oldbuf,maxlen,prec,prec2);
					int initial=1;
					if (len==0) return 0;
					maxlen-=len; buf+=len;
					if (maxlen>0) {
						*buf='e';
						++buf;
					}
					--maxlen;
					for (len=1000; len>0; len/=10) {
						if (e10>=len || !initial) {
							if (maxlen>0) {
								*buf=(e10/len)+'0';
								++buf;
							}
							--maxlen;
							initial=0;
							e10=e10%len;
						}
					}
					if (maxlen>0) goto fini;
					return 0;
				}
				d-=digit*tmp;
				--maxlen;
			}
			tmp/=10.0;
		}
	}
	else
	{
		tmp = 0.1;
	}

	if (buf==oldbuf) {
		if (!maxlen) return 0; --maxlen;
		*buf='0'; ++buf;
	}
	if (prec2 || prec>(unsigned int)(buf-oldbuf)+1) {	/* more digits wanted */
		if (!maxlen) return 0; --maxlen;
		*buf='.'; ++buf;
		prec-=buf-oldbuf-1;
		if (prec2) prec=prec2;
		if (prec>maxlen) return 0;
		while (prec>0) {
			char digit;
			double fraction=d/tmp;
			digit=(int)(fraction);		/* floor() */
			*buf=digit+'0'; ++buf;
			d-=digit*tmp;
			tmp/=10.0;
			--prec;
		}
	}
fini:
	*buf=0;
	return buf-oldbuf;
}