Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

Double.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file Double.d
00004         
00005         Copyright (c) 2004 Kris Bell
00006         
00007         This software is provided 'as-is', without any express or implied
00008         warranty. In no event will the authors be held liable for damages
00009         of any kind arising from the use of this software.
00010         
00011         Permission is hereby granted to anyone to use this software for any 
00012         purpose, including commercial applications, and to alter it and/or 
00013         redistribute it freely, subject to the following restrictions:
00014         
00015         1. The origin of this software must not be misrepresented; you must 
00016            not claim that you wrote the original software. If you use this 
00017            software in a product, an acknowledgment within documentation of 
00018            said product would be appreciated but is not required.
00019 
00020         2. Altered source versions must be plainly marked as such, and must 
00021            not be misrepresented as being the original software.
00022 
00023         3. This notice may not be removed or altered from any distribution
00024            of the source.
00025 
00026         4. Derivative works are permitted, but they must carry this notice
00027            in full and credit the original source.
00028 
00029 
00030                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00031 
00032         
00033         @version        Initial version, Feb 2005
00034         @author         Kris
00035 
00036 
00037 *******************************************************************************/
00038 
00039 module mango.format.Double;
00040 
00041 private import  mango.format.Int,
00042                 mango.format.Number;
00043 
00044 extern (C) double frexp (double, int*);
00045 extern (C) int memicmp  (char*, char*, uint);
00046 
00047 /******************************************************************************
00048 
00049         A set of functions for converting between string and numeric 
00050         values. 
00051 
00052         Used by modules Token and DisplayWriter
00053 
00054 ******************************************************************************/
00055 
00056 class Double : Number
00057 {
00058         alias double tValue;
00059 
00060         /**********************************************************************
00061 
00062         **********************************************************************/
00063 
00064         private static tValue pow10 (uint exp)
00065         in {
00066            assert (exp < 512);
00067            }    
00068         body
00069         {
00070                 static  tValue[] Powers = 
00071                         [
00072                         1.0e1,
00073                         1.0e2,
00074                         1.0e4,
00075                         1.0e8,
00076                         1.0e16,
00077                         1.0e32,
00078                         1.0e64,
00079                         1.0e128,
00080                         1.0e256,
00081                         ];
00082 
00083                 tValue mult = 1.0;
00084                 foreach (tValue power; Powers)
00085                         {
00086                         if (exp & 1)
00087                             mult *= power;
00088                         if ((exp >>= 1) == 0)
00089                              break;
00090                         }
00091                 return mult;
00092         }
00093 
00094         /**********************************************************************
00095 
00096                 Convert a formatted string of digits to a floating-
00097                 point number. Good for general use, but use David
00098                 Gay's dtoa package if serious rounding adjustments
00099                 should be applied.
00100 
00101         **********************************************************************/
00102 
00103         final static tValue parse (tChar[] src, uint* ate=null)
00104         {
00105                 char            c;
00106                 char*           p;
00107                 int             exp;
00108                 bool            sign;
00109                 tValue          value = 0.0;
00110 
00111                 // remove leading space, and sign
00112                 c = *(p = src.ptr + trim (src, sign));
00113 
00114                 // read leading digits; note that leading
00115                 // zeros are simply multiplied away.
00116                 while (c >= '0' && c <= '9')
00117                       {
00118                       value = value * 10 + (c - '0');
00119                       c = *++p;
00120                       }
00121 
00122                 // gobble up the point
00123                 if (c == '.')
00124                     c = *++p;
00125 
00126                 // read fractional digits; note that we accumulate
00127                 // all digits ... very long numbers impact accuracy
00128                 // to a degree, but perhaps not as much as one might
00129                 // expect. A prior version limited the digit count,
00130                 // but did not show marked improvement. For maximum
00131                 // accuracy when reading and writing, use David Gay's
00132                 // dtoa package instead.
00133                 while (c >= '0' && c <= '9')
00134                       {
00135                       value = value * 10 + (c - '0');
00136                       c = *++p;
00137                       --exp;
00138                       } 
00139 
00140                 // did we get something?
00141                 if (value)
00142                    {
00143                    // parse base10 exponent?
00144                    if (c == 'e' || c == 'E')
00145                       {
00146                       uint len;
00147                       exp += Int.parse (src[(p-src.ptr)+1..length], Radix.Decimal, &len);
00148                       p += len;
00149                       }
00150 
00151                    // adjust mantissa; note that the exponent has
00152                    // already been adjusted for fractional digits
00153                    if (exp < 0)
00154                        value /= pow10 (-exp);
00155                    else
00156                       value *= pow10 (exp);
00157                    }
00158                 else
00159                    // was it was nan instead?
00160                    if (p == src.ptr)
00161                        if (memicmp (p, "inf", 3) == 0)
00162                            p += 3, value = tValue.infinity;
00163                        else
00164                           if (memicmp (p, "nan", 3) == 0)
00165                               p += 3, value = tValue.nan;
00166 
00167                 // set parse length, and return value
00168                 if (ate)
00169                     *ate = p - src.ptr;
00170                 return sign ? -value : value; 
00171         }
00172 
00173 
00174         /**********************************************************************
00175 
00176                 Convert a float to a string. This produces pretty
00177                 good results for the most part, though one should
00178                 use David Gay's dtoa package for best accuracy.
00179 
00180                 Note that the approach first normalizes a base10
00181                 mantissa, then pulls digits from the left side
00182                 whilst emitting them (rightward) to the output.
00183 
00184         **********************************************************************/
00185 
00186         static final tChar[] format (tChar[] dst, tValue x, uint decimals = 6, bool scientific = false)
00187         in {
00188            assert (dst.length >= 32);
00189            }
00190         body
00191         {
00192                 // function to strip digits from the
00193                 // left of a normalized base-10 number
00194                 static int toDigit (inout tValue v, inout int count)
00195                 {
00196                         int digit;
00197 
00198                         // double can reliably hold 17 digits only
00199                         if (++count > 17)
00200                             digit = 0;
00201                         else
00202                            {
00203                            // remove leading digit, and bump
00204                            digit = cast(int) v;
00205                            v = (v - digit) * 10.0;
00206                            }
00207                         return digit + '0';
00208                 }
00209 
00210                 // test for nan/infinity
00211                 if (((cast(ushort*) &x)[3] & 0x7ff0) == 0x7ff0)
00212                       if (*(cast(ulong*) &x) & 0x000f_ffff_ffff_ffff)
00213                             return "nan";
00214                       else
00215                          return "inf";
00216 
00217                 int exp;
00218                 bool sign;
00219 
00220                 // extract the sign
00221                 if (x < 0.0)
00222                    {
00223                    x = -x;
00224                    sign = true;
00225                    }
00226 
00227                 // don't scale if zero
00228                 if (x > 0.0)
00229                    {
00230                    // round up a bit (should do even/odd test?)
00231                    x += 0.5 / pow10 (decimals);
00232 
00233                    // extract exponent; convert to base10
00234                    frexp (x, &exp);
00235                    exp = cast(int) (0.301029995664 * exp);
00236 
00237                    // normalize base10 mantissa (0 < m < 10)
00238                    int len = exp;
00239                    if (exp < 0)
00240                       {
00241                       --exp;
00242                       x *= pow10 (len = -exp);
00243                       }
00244                    else
00245                       x /= pow10 (exp);
00246 
00247                    // switch to short display if not enough space
00248                    if (len + 32 > dst.length)
00249                        scientific = true;
00250                    }
00251 
00252                 char*p = dst;
00253                 int count = 0;
00254 
00255                 // emit sign
00256                 if (sign)
00257                     *p++ = '-';
00258 
00259                 // are we doing +/-exp format?
00260                 if (scientific)
00261                    {
00262                    // emit first digit, and decimal point
00263                    *p++ = toDigit (x, count);
00264                    *p++ = '.';
00265 
00266                    // emit rest of mantissa
00267                    while (decimals-- > 0)
00268                           *p++ = toDigit (x, count);
00269 
00270                    // emit exponent, if non zero
00271                    if (exp)
00272                       {
00273                       *p++ = 'e';
00274                       *p++ = (exp < 0) ? '-' : '+';
00275                       if (exp < 0)
00276                           exp = -exp;
00277 
00278                       if (exp >= 100)
00279                          {
00280                          *p++ = (exp/100) + '0';
00281                          exp %= 100;
00282                          }
00283 
00284                       *p++ = (exp/10) + '0';
00285                       *p++ = (exp%10) + '0';
00286                       }
00287                    }
00288                 else
00289                    {
00290                    // if fraction only, emit a leading zero
00291                    if (exp < 0)
00292                        *p++ = '0';
00293                    else
00294                       // emit all digits to the left of point
00295                       for (; exp >= 0; --exp)
00296                              *p++ = toDigit (x, count);
00297 
00298                    // emit point
00299                    *p++ = '.';
00300 
00301                    // emit leading fractional zeros?
00302                    for (++exp; exp < 0 && decimals > 0; --decimals, ++exp)
00303                         *p++ = '0';
00304 
00305                    // output remaining digits, if any. Trailing
00306                    // zeros are also returned from toDigit()
00307                    while (decimals-- > 0)
00308                           *p++ = toDigit (x, count);
00309                    }
00310 
00311                 return dst [0..(p - dst.ptr)];
00312         }
00313 
00314 
00315 /+                
00316         /**********************************************************************
00317 
00318                 version of parse() with a limit on the digits read
00319 
00320         **********************************************************************/
00321 
00322         version (ReadLimit)
00323         {
00324                 int       len;
00325                 const int MaxDigits = 17;
00326 
00327                 p = d.ptr + trimZero (d) - 1;
00328 
00329                 while ((c = *++p) >= '0' && c <= '9')
00330                 {
00331                         if (++len < MaxDigits)
00332                                 value = value * 10 + (c - '0');
00333                         else 
00334                          // we're still accumulating!
00335                                 ++exp;
00336                 }
00337 
00338                 if (c == '.')
00339                 {
00340                    // ignore leading zeros unless 
00341                    // we already have some digits
00342                         if (value == 0)
00343                         {
00344                                 while (*++p == '0')
00345                                         --exp;
00346                                 --p;
00347                         }
00348 
00349                         while ((c = *++p) >= '0' && c <= '9')
00350                         {
00351                                 if (++len < MaxDigits)
00352                                 {
00353                                         --exp;
00354                                         value = value * 10 + (c - '0');
00355                                 }
00356                         } 
00357                 }
00358 
00359                 if (len == 0)
00360                 {
00361                         if (c != '0' && d.length >= 3)
00362                                 if (d == "inf")
00363                                         return tValue.infinity;
00364                                 else
00365                                         if (d == "nan")
00366                                                 return tValue.nan;
00367                         return 0.0 * sign;
00368                 }
00369 
00370                 if (c == 'e' || c == 'E')
00371                         exp += strtoi (d[(p-d.ptr)+1..length]);
00372         }
00373 +/
00374 }
00375 
00376 

Generated on Sun Mar 6 00:30:56 2005 for Mango by doxygen 1.3.6