+/*
+Copyright (C) 1990 Free Software Foundation
+ written by Doug Lea (dl@rocky.oswego.edu)
+
+This file is part of GNU CC.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY. No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing. Refer to the GNU CC General Public
+License for full details.
+
+Everyone is granted permission to copy, modify and redistribute
+GNU CC, but only under the conditions described in the
+GNU CC General Public License. A copy of this license is
+supposed to have been given to you along with GNU CC so you
+can know your rights and responsibilities. It should be in a
+file named COPYING. Among other things, the copyright notice
+and this notice must be preserved on all copies.
+*/
+
+#ifdef __GNUG__
+#pragma implementation
+#endif
+#include <builtin.h>
+#include <math.h>
+#include <values.h>
+#include <AllocRing.h>
+
+extern AllocRing _libgxx_fmtq;
+
+char* dtoa(double fpnum, char cvt, int width, int prec)
+{
+ // set up workspace
+
+ // max possible digits <= those need to show all of prec + exp
+ // <= ceil(log10(HUGE)) plus space for null, etc.
+
+ const int worksiz = int((M_LN2 / M_LN10) * DMAXEXP) + 8;
+
+ // for fractional part
+ char fwork[worksiz];
+ char* fw = fwork;
+
+ // for integer part
+ char iwork[worksiz];
+ char* iworkend = &iwork[sizeof(iwork) - 1];
+ char* iw = iworkend;
+ *iw = 0;
+
+ // for exponent part
+
+ const int eworksiz = int(M_LN2 * _DEXPLEN) + 8;
+ char ework[eworksiz];
+ char* eworkend = &ework[sizeof(ework) - 1];
+ char* ew = eworkend;
+ *ew = 0;
+
+#if (_IEEE != 0)
+ if (isinf(fpnum))
+ {
+ char* inffmt = (char *) _libgxx_fmtq.alloc(5);
+ char* inffmtp = inffmt;
+ if (fpnum < 0)
+ *inffmtp++ = '-';
+ strcpy(inffmtp, "Inf");
+ return inffmt;
+ }
+
+ if (isnan(fpnum))
+ {
+ char* nanfmt = (char *) _libgxx_fmtq.alloc(4);
+ strcpy(nanfmt, "NaN");
+ return nanfmt;
+ }
+#endif
+
+ // grab sign & make non-negative
+ int is_neg = fpnum < 0;
+ if (is_neg) fpnum = -fpnum;
+
+ // precision matters
+
+ if (prec > worksiz - 2) // can't have more prec than supported
+ prec = worksiz - 2;
+
+ double powprec;
+ if (prec == 6)
+ powprec = 1.0e6;
+ else
+ powprec = pow(10.0, (long) prec);
+
+ double rounder = 0.5 / powprec;
+
+ int f_fmt = cvt == 'f' ||
+ ((cvt == 'g') && (fpnum == 0.0 || (fpnum >= 1e-4 && fpnum < powprec)));
+
+ int iwidth = 0;
+ int fwidth = 0;
+ int ewidth = 0;
+
+ if (f_fmt) // fixed format
+ {
+ double ipart;
+ double fpart = modf(fpnum, &ipart);
+
+ // convert fractional part
+
+ if (fpart >= rounder || cvt != 'g')
+ {
+ fpart += rounder;
+ if (fpart >= 1.0)
+ {
+ ipart += 1.0;
+ fpart -= 1.0;
+ }
+ double ffpart = fpart;
+ double ifpart;
+ for (int i = 0; i < prec; ++i)
+ {
+ ffpart = modf(ffpart * 10.0, &ifpart);
+ *fw++ = '0' + int(ifpart);
+ ++fwidth;
+ }
+ if (cvt == 'g') // inhibit trailing zeroes if g-fmt
+ {
+ for (char* p = fw - 1; p >= fwork && *p == '0'; --p)
+ {
+ *p = 0;
+ --fwidth;
+ }
+ }
+ }
+
+ // convert integer part
+ if (ipart == 0.0)
+ {
+ if (cvt != 'g' || fwidth < prec || fwidth < width)
+ {
+ *--iw = '0'; ++iwidth;
+ }
+ }
+ else if (ipart <= double(MAXLONG)) // a useful speedup
+ {
+ long li = long(ipart);
+ while (li != 0)
+ {
+ *--iw = '0' + (li % 10);
+ li = li / 10;
+ ++iwidth;
+ }
+ }
+ else // the slow way
+ {
+ while (ipart > 0.5)
+ {
+ double ff = modf(ipart / 10.0, &ipart);
+ ff = (ff + 0.05) * 10.0;
+ *--iw = '0' + int(ff);
+ ++iwidth;
+ }
+ }
+
+ // g-fmt: kill part of frac if prec/width exceeded
+ if (cvt == 'g')
+ {
+ int m = prec;
+ if (m < width)
+ m = width;
+ int adj = iwidth + fwidth - m;
+ if (adj > fwidth)
+ adj = fwidth;
+ if (adj > 0)
+ {
+ for (char* f = &fwork[fwidth-1]; f >= fwork && adj > 0; --adj, --f)
+ {
+ --fwidth;
+ char ch = *f;
+ *f = 0;
+ if (ch > '5') // properly round: unavoidable propagation
+ {
+ int carry = 1;
+ for (char* p = f - 1; p >= fwork && carry; --p)
+ {
+ ++*p;
+ if (*p > '9')
+ *p = '0';
+ else
+ carry = 0;
+ }
+ if (carry)
+ {
+ for (p = iworkend - 1; p >= iw && carry; --p)
+ {
+ ++*p;
+ if (*p > '9')
+ *p = '0';
+ else
+ carry = 0;
+ }
+ if (carry)
+ {
+ *--iw = '1';
+ ++iwidth;
+ --adj;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ else // e-fmt
+ {
+
+ // normalize
+ int exp = 0;
+ while (fpnum >= 10.0)
+ {
+ fpnum *= 0.1;
+ ++exp;
+ }
+ while (fpnum > 0.0 && fpnum < 1.0)
+ {
+ fpnum *= 10.0;
+ --exp;
+ }
+
+ double ipart;
+ double fpart = modf(fpnum, &ipart);
+
+
+ if (cvt == 'g') // used up one digit for int part...
+ {
+ --prec;
+ powprec /= 10.0;
+ rounder = 0.5 / powprec;
+ }
+
+ // convert fractional part -- almost same as above
+ if (fpart >= rounder || cvt != 'g')
+ {
+ fpart += rounder;
+ if (fpart >= 1.0)
+ {
+ fpart -= 1.0;
+ ipart += 1.0;
+ if (ipart >= 10.0)
+ {
+ ++exp;
+ ipart /= 10.0;
+ fpart /= 10.0;
+ }
+ }
+ double ffpart = fpart;
+ double ifpart;
+ for (int i = 0; i < prec; ++i)
+ {
+ ffpart = modf(ffpart * 10.0, &ifpart);
+ *fw++ = '0' + int(ifpart);
+ ++fwidth;
+ }
+ if (cvt == 'g') // inhibit trailing zeroes if g-fmt
+ {
+ for (char* p = fw - 1; p >= fwork && *p == '0'; --p)
+ {
+ *p = 0;
+ --fwidth;
+ }
+ }
+ }
+
+
+ // convert exponent
+
+ char eneg = exp < 0;
+ if (eneg) exp = - exp;
+
+ while (exp > 0)
+ {
+ *--ew = '0' + (exp % 10);
+ exp /= 10;
+ ++ewidth;
+ }
+
+ while (ewidth < 2) // ensure at least 2 zeroes
+ {
+ *--ew = '0';
+ ++ewidth;
+ }
+
+ *--ew = eneg ? '-' : '+';
+ *--ew = 'e';
+
+ ewidth += 2;
+
+ // convert the one-digit integer part
+ *--iw = '0' + int(ipart);
+ ++iwidth;
+
+ }
+
+ // arrange everything in returned string
+
+ int showdot = cvt != 'g' || fwidth > 0;
+
+ int fmtwidth = is_neg + iwidth + showdot + fwidth + ewidth;
+
+ int pad = width - fmtwidth;
+ if (pad < 0) pad = 0;
+
+ char* fmtbase = (char *) _libgxx_fmtq.alloc(fmtwidth + pad + 1);
+ char* fmt = fmtbase;
+
+ for (int i = 0; i < pad; ++i) *fmt++ = ' ';
+
+ if (is_neg) *fmt++ = '-';
+
+ for (i = 0; i < iwidth; ++i) *fmt++ = *iw++;
+
+ if (showdot)
+ {
+ *fmt++ = '.';
+ fw = fwork;
+ for (i = 0; i < fwidth; ++i) *fmt++ = *fw++;
+ }
+
+ for (i = 0; i < ewidth; ++i) *fmt++ = *ew++;
+
+ *fmt = 0;
+
+ return fmtbase;
+}
+