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

Generated on Sat Apr 9 20:11:25 2005 for Mango by doxygen 1.3.6