home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / progc / djlsr106.arj / DTOA.CC < prev    next >
C/C++ Source or Header  |  1992-03-24  |  7KB  |  333 lines

  1. /* 
  2. Copyright (C) 1990 Free Software Foundation
  3.     written by Doug Lea (dl@rocky.oswego.edu)
  4.  
  5. This file is part of the GNU C++ Library.  This library is free
  6. software; you can redistribute it and/or modify it under the terms of
  7. the GNU Library General Public License as published by the Free
  8. Software Foundation; either version 2 of the License, or (at your
  9. option) any later version.  This library is distributed in the hope
  10. that it will be useful, but WITHOUT ANY WARRANTY; without even the
  11. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  12. PURPOSE.  See the GNU Library General Public License for more details.
  13. You should have received a copy of the GNU Library General Public
  14. License along with this library; if not, write to the Free Software
  15. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  16. */
  17.  
  18. #ifdef __GNUG__
  19. #pragma implementation
  20. #endif
  21. #include <builtin.h>
  22. #include <math.h>
  23. #include <values.h>
  24. #include <AllocRing.h>
  25.  
  26. extern AllocRing _libgxx_fmtq;
  27.  
  28. char* dtoa(double fpnum,  char cvt, int width, int prec)
  29. {
  30.   // set up workspace
  31.  
  32.   // max possible digits <= those need to show all of prec + exp
  33.   // <= ceil(log10(HUGE)) plus space for null, etc.
  34.  
  35.   const int worksiz = int((M_LN2 / M_LN10) * DMAXEXP) + 8; 
  36.  
  37.   // for fractional part
  38.   char  fwork[worksiz];
  39.   char* fw = fwork;
  40.  
  41.   // for integer part
  42.   char  iwork[worksiz];
  43.   char* iworkend = &iwork[sizeof(iwork) - 1];
  44.   char* iw = iworkend;
  45.   *iw = 0;
  46.  
  47.   // for exponent part
  48.  
  49.   const int eworksiz = int(M_LN2 * _DEXPLEN) + 8;
  50.   char  ework[eworksiz];
  51.   char* eworkend = &ework[sizeof(ework) - 1];
  52.   char* ew = eworkend;
  53.   *ew = 0;
  54.  
  55. #if (_IEEE != 0)
  56.   if (isinf(fpnum))
  57.   {
  58.     char* inffmt = (char *) _libgxx_fmtq.alloc(5);
  59.     char* inffmtp = inffmt;
  60.     if (fpnum < 0)
  61.       *inffmtp++ = '-';
  62.     strcpy(inffmtp, "Inf");
  63.     return inffmt;
  64.   }
  65.  
  66.   if (isnan(fpnum))
  67.   {
  68.     char* nanfmt = (char *) _libgxx_fmtq.alloc(4);
  69.     strcpy(nanfmt, "NaN");
  70.     return nanfmt;
  71.   }
  72. #endif
  73.  
  74.   // grab sign & make non-negative
  75.   int is_neg = fpnum < 0;
  76.   if (is_neg) fpnum = -fpnum;
  77.  
  78.   // precision matters
  79.  
  80.   if (prec > worksiz - 2) // can't have more prec than supported
  81.     prec = worksiz - 2;
  82.   
  83.   double powprec;
  84.   if (prec == 6)
  85.     powprec = 1.0e6;
  86.   else
  87.     powprec = pow(10.0, (long) prec);
  88.  
  89.   double rounder = 0.5 / powprec;
  90.  
  91.   int f_fmt = cvt == 'f' ||
  92.     ((cvt == 'g') && (fpnum == 0.0 || (fpnum >= 1e-4 && fpnum < powprec)));
  93.  
  94.   int iwidth = 0;
  95.   int fwidth = 0;
  96.   int ewidth = 0;
  97.  
  98.   if (f_fmt)  // fixed format
  99.   {
  100.     double ipart;
  101.     double fpart = modf(fpnum, &ipart);
  102.  
  103.     // convert fractional part
  104.  
  105.     if (fpart >= rounder || cvt != 'g')
  106.     {
  107.       fpart += rounder;
  108.       if (fpart >= 1.0)
  109.       {
  110.         ipart += 1.0;
  111.         fpart -= 1.0;
  112.       }
  113.       double ffpart = fpart;
  114.       double ifpart;
  115.       for (int i = 0; i < prec; ++i)
  116.       {
  117.         ffpart = modf(ffpart * 10.0, &ifpart);
  118.         *fw++ = '0' + int(ifpart);
  119.         ++fwidth;
  120.       }
  121.       if (cvt == 'g')  // inhibit trailing zeroes if g-fmt
  122.       {
  123.         for (char* p = fw - 1; p >= fwork && *p == '0'; --p)
  124.         {
  125.           *p = 0;
  126.           --fwidth;
  127.         }
  128.       }
  129.     }
  130.  
  131.     // convert integer part
  132.     if (ipart == 0.0)
  133.     {
  134.       if (cvt != 'g' || fwidth < prec || fwidth < width)
  135.       {
  136.         *--iw = '0'; ++iwidth;
  137.       }
  138.     }
  139.     else if (ipart <= double(MAXLONG)) // a useful speedup
  140.     {
  141.       long li = long(ipart);
  142.       while (li != 0)
  143.       {
  144.         *--iw = '0' + (li % 10);
  145.         li = li / 10;
  146.         ++iwidth;
  147.       }
  148.     }
  149.     else // the slow way
  150.     {
  151.       while (ipart > 0.5)
  152.       {
  153.         double ff = modf(ipart / 10.0, &ipart);
  154.         ff = (ff + 0.05) * 10.0;
  155.         *--iw = '0' + int(ff);
  156.         ++iwidth;
  157.       }
  158.     }
  159.  
  160.     // g-fmt: kill part of frac if prec/width exceeded
  161.     if (cvt == 'g')
  162.     {
  163.       int m = prec;
  164.       if (m < width)
  165.         m = width;
  166.       int adj = iwidth + fwidth - m;
  167.       if (adj > fwidth)
  168.         adj = fwidth;
  169.       if (adj > 0)
  170.       {
  171.         for (char* f = &fwork[fwidth-1]; f >= fwork && adj > 0; --adj, --f)
  172.         {
  173.           --fwidth;
  174.           char ch = *f;
  175.           *f = 0;
  176.           if (ch > '5') // properly round: unavoidable propagation
  177.           {
  178.             int carry = 1;
  179.             for (char* p = f - 1; p >= fwork && carry; --p)
  180.             {
  181.               ++*p;
  182.               if (*p > '9')
  183.                 *p = '0';
  184.               else
  185.                 carry = 0;
  186.             }
  187.             if (carry)
  188.             {
  189.               for (p = iworkend - 1; p >= iw && carry; --p)
  190.               {
  191.                 ++*p;
  192.                 if (*p > '9')
  193.                   *p = '0';
  194.                 else
  195.                   carry = 0;
  196.               }
  197.               if (carry)
  198.               {
  199.                 *--iw = '1';
  200.                 ++iwidth;
  201.                 --adj;
  202.               }
  203.             }
  204.           }
  205.         }
  206.       }
  207.     }
  208.               
  209.   }
  210.   else  // e-fmt
  211.   {
  212.     
  213.     // normalize
  214.     int exp = 0;
  215.     while (fpnum >= 10.0)
  216.     {
  217.       fpnum *= 0.1;
  218.       ++exp;
  219.     }
  220.     double almost_one = 1.0 - rounder;
  221.     while (fpnum > 0.0 && fpnum < almost_one)
  222.     {
  223.       fpnum *= 10.0;
  224.       --exp;
  225.     }
  226.     
  227.     double ipart;
  228.     double fpart = modf(fpnum, &ipart);
  229.  
  230.  
  231.     if (cvt == 'g')     // used up one digit for int part...
  232.     {
  233.       --prec;
  234.       powprec /= 10.0;
  235.       rounder = 0.5 / powprec;
  236.     }
  237.  
  238.     // convert fractional part -- almost same as above
  239.     if (fpart >= rounder || cvt != 'g')
  240.     {
  241.       fpart += rounder;
  242.       if (fpart >= 1.0)
  243.       {
  244.         fpart -= 1.0;
  245.         ipart += 1.0;
  246.         if (ipart >= 10.0)
  247.         {
  248.           ++exp;
  249.           ipart /= 10.0;
  250.           fpart /= 10.0;
  251.         }
  252.       }
  253.       double ffpart = fpart;
  254.       double ifpart;
  255.       for (int i = 0; i < prec; ++i)
  256.       {
  257.         ffpart = modf(ffpart * 10.0, &ifpart);
  258.         *fw++ = '0' + int(ifpart);
  259.         ++fwidth;
  260.       }
  261.       if (cvt == 'g')  // inhibit trailing zeroes if g-fmt
  262.       {
  263.         for (char* p = fw - 1; p >= fwork && *p == '0'; --p)
  264.         {
  265.           *p = 0;
  266.           --fwidth;
  267.         }
  268.       }
  269.     }
  270.  
  271.     
  272.     // convert exponent
  273.  
  274.     char eneg = exp < 0;
  275.     if (eneg) exp = - exp;
  276.  
  277.     while (exp > 0)
  278.     {
  279.       *--ew = '0' + (exp % 10);
  280.       exp /= 10;
  281.       ++ewidth;
  282.     }
  283.  
  284.     while (ewidth < 2)  // ensure at least 2 zeroes
  285.     {
  286.       *--ew = '0';
  287.       ++ewidth;
  288.     }
  289.  
  290.     *--ew = eneg ? '-' : '+';
  291.     *--ew = 'e';
  292.  
  293.     ewidth += 2;
  294.  
  295.     // convert the one-digit integer part
  296.     *--iw = '0' + int(ipart);
  297.     ++iwidth;
  298.     
  299.   }
  300.  
  301.   // arrange everything in returned string
  302.  
  303.   int showdot = cvt != 'g' || fwidth > 0;
  304.  
  305.   int fmtwidth = is_neg + iwidth + showdot + fwidth + ewidth;
  306.   
  307.   int pad = width - fmtwidth;
  308.   if (pad < 0) pad = 0;
  309.   
  310.   char* fmtbase = (char *) _libgxx_fmtq.alloc(fmtwidth + pad + 1);
  311.   char* fmt = fmtbase;
  312.   
  313.   for (int i = 0; i < pad; ++i) *fmt++ = ' ';
  314.   
  315.   if (is_neg) *fmt++ = '-';
  316.   
  317.   for (i = 0; i < iwidth; ++i) *fmt++ = *iw++;
  318.   
  319.   if (showdot)
  320.   {
  321.     *fmt++ = '.';
  322.     fw = fwork;
  323.     for (i = 0; i < fwidth; ++i) *fmt++ = *fw++;
  324.   }
  325.   
  326.   for (i = 0; i < ewidth; ++i) *fmt++ = *ew++;
  327.   
  328.   *fmt = 0;
  329.   
  330.   return fmtbase;
  331. }
  332.  
  333.