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

Generated on Sat Dec 24 17:28:32 2005 for Mango by  doxygen 1.4.0