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

Generated on Fri Nov 11 18:44:19 2005 for Mango by  doxygen 1.4.0