source: trunk/sys/dietlibc/__dtostr.c @ 21

Last change on this file since 21 was 1, checked in by alain, 8 years ago

First import

File size: 3.8 KB
Line 
1#include <stdlib.h>
2#include <endian.h>
3#include <math.h>
4/* convert double to string.  Helper for sprintf. */
5
6static int copystring(char* buf,int maxlen, const char* s) {
7  int i;
8  for (i=0; i<3&&i<maxlen; ++i)
9    buf[i]=s[i];
10  if (i<maxlen) { buf[i]=0; ++i; }
11  return i;
12}
13
14int __dtostr(double d,char *buf,unsigned int maxlen,unsigned int prec,unsigned int prec2,int g) {
15
16  union {
17    unsigned long long l;
18    double d;
19  } u = { .d=d };
20#if 1
21  /* step 1: extract sign, mantissa and exponent */
22  signed long e=((u.l>>52)&((1<<11)-1))-1023;
23#else
24#if __BYTE_ORDER == __LITTLE_ENDIAN
25  signed long e=(((((unsigned long*)&d)[1])>>20)&((1<<11)-1))-1023;
26#else
27  signed long e=(((*((unsigned long*)&d))>>20)&((1<<11)-1))-1023;
28#endif
29#endif
30/*  unsigned long long m=u.l & ((1ull<<52)-1); */
31  /* step 2: exponent is base 2, compute exponent for base 10 */
32  signed long e10;
33  /* step 3: calculate 10^e10 */
34  unsigned int i;
35  double backup=d;
36  double tmp;
37  char *oldbuf=buf;
38
39  if ((i=isinf(d))) return copystring(buf,maxlen,i>0?"inf":"-inf");
40  if (isnan(d)) return copystring(buf,maxlen,"nan");
41  e10=1+(long)(e*0.30102999566398119802); /* log10(2) */
42  /* Wir iterieren von Links bis wir bei 0 sind oder maxlen erreicht
43   * ist.  Wenn maxlen erreicht ist, machen wir das nochmal in
44   * scientific notation.  Wenn dann von prec noch was übrig ist, geben
45   * wir einen Dezimalpunkt aus und geben prec2 Nachkommastellen aus.
46   * Wenn prec2 Null ist, geben wir so viel Stellen aus, wie von prec
47   * noch übrig ist. */
48  if (d==0.0) {
49    prec2=prec2==0?1:prec2+2;
50    prec2=prec2>maxlen?8:prec2;
51    i=0;
52    if (prec2 && (long long)u.l<0) { buf[0]='-'; ++i; }
53    for (; i<prec2; ++i) buf[i]='0';
54    buf[buf[0]=='0'?1:2]='.'; buf[i]=0;
55    return i;
56  }
57
58  if (d < 0.0) { d=-d; *buf='-'; --maxlen; ++buf; }
59
60   /*
61      Perform rounding. It needs to be done before we generate any
62      digits as the carry could propagate through the whole number.
63   */
64
65  tmp = 0.5;
66  for (i = 0; i < prec2; i++) { tmp *= 0.1; }
67  d += tmp;
68
69  if (d < 1.0) { *buf='0'; --maxlen; ++buf; }
70/*  printf("e=%d e10=%d prec=%d\n",e,e10,prec); */
71  if (e10>0) {
72    int first=1;        /* are we about to write the first digit? */
73    tmp = 10.0;
74    i=e10;
75    while (i>10) { tmp=tmp*1e10; i-=10; }
76    while (i>1) { tmp=tmp*10; --i; }
77    /* the number is greater than 1. Iterate through digits before the
78     * decimal point until we reach the decimal point or maxlen is
79     * reached (in which case we switch to scientific notation). */
80    while (tmp>0.9) {
81      char digit;
82      double fraction=d/tmp;
83        digit=(int)(fraction);          /* floor() */
84      if (!first || digit) {
85        first=0;
86        *buf=digit+'0'; ++buf;
87        if (!maxlen) {
88          /* use scientific notation */
89          int len=__dtostr(backup/tmp,oldbuf,maxlen,prec,prec2,0);
90          int initial=1;
91          if (len==0) return 0;
92          maxlen-=len; buf+=len;
93          if (maxlen>0) {
94            *buf='e';
95            ++buf;
96          }
97          --maxlen;
98          for (len=1000; len>0; len/=10) {
99            if (e10>=len || !initial) {
100              if (maxlen>0) {
101                *buf=(e10/len)+'0';
102                ++buf;
103              }
104              --maxlen;
105              initial=0;
106              e10=e10%len;
107            }
108          }
109          if (maxlen>0) goto fini;
110          return 0;
111        }
112        d-=digit*tmp;
113        --maxlen;
114      }
115      tmp/=10.0;
116    }
117  }
118  else
119  {
120     tmp = 0.1;
121  }
122
123  if (buf==oldbuf) {
124    if (!maxlen) return 0; --maxlen;
125    *buf='0'; ++buf;
126  }
127  if (prec2 || prec>(unsigned int)(buf-oldbuf)+1) {     /* more digits wanted */
128    if (!maxlen) return 0; --maxlen;
129    *buf='.'; ++buf;
130    if (g) {
131      if (prec2) prec=prec2;
132      prec-=buf-oldbuf-1;
133    } else {
134      prec-=buf-oldbuf-1;
135      if (prec2) prec=prec2;
136    }
137    if (prec>maxlen) return 0;
138    while (prec>0) {
139      char digit;
140      double fraction=d/tmp;
141      digit=(int)(fraction);            /* floor() */
142      *buf=digit+'0'; ++buf;
143      d-=digit*tmp;
144      tmp/=10.0;
145      --prec;
146    }
147  }
148fini:
149  *buf=0;
150  return buf-oldbuf;
151}
Note: See TracBrowser for help on using the repository browser.