home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Black Box 4
/
BlackBox.cdr
/
progc
/
djlsr106.arj
/
DTOA.CC
< prev
next >
Wrap
C/C++ Source or Header
|
1992-03-24
|
7KB
|
333 lines
/*
Copyright (C) 1990 Free Software Foundation
written by Doug Lea (dl@rocky.oswego.edu)
This file is part of the GNU C++ Library. This library is free
software; you can redistribute it and/or modify it under the terms of
the GNU Library General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version. This library is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#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;
}
double almost_one = 1.0 - rounder;
while (fpnum > 0.0 && fpnum < almost_one)
{
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;
}