Changeset 160 for trunk/meta/conv.d

Show
Ignore:
Timestamp:
03/09/06 08:25:20 (2 years ago)
Author:
Don Clugston
Message:

Many accumulated changes to 'meta'. The most interesting is the one in feqtest, which tests floating point numbers for equality to the number of decimal places given in a string.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/meta/conv.d

    r126 r160  
    66  * Author: Don Clugston. License: Public domain. 
    77  */ 
     8/* 
     9Normally, a function which processes the first part of a string 
     10would return the part which was not consumed (the 'tail'). 
     11This doesn't work very well for D templates, so for such functions, 
     12we supply a seperate metafunction with the suffix 'Consumed' which returns 
     13the  number of characters consumed. (This is the same as the index of the first 
     14character in the string which was not consumed). 
     15This results in some code duplication, but seems to results in a much cleaner design. 
     16*/ 
    817   
    918module meta.conv; 
     
    2029template itoa(long n) 
    2130{ 
    22     static if (n<0) { 
    23         const char [] itoa = "-" ~ .itoa!(-n);  
    24     } else static if (n<10L) { 
     31    static if (n<0) 
     32        const char [] itoa = "-" ~ itoa!(-n);  
     33    else static if (n<10L) 
    2534        const char [] itoa = decimaldigit!(n); 
    26     } else { 
     35    else 
    2736        const char [] itoa = itoa!(n/10L) ~ decimaldigit!(n%10L); 
    28     } 
    2937} 
    3038 
    3139template toHexString(ulong n) 
    3240{ 
    33     static if (n<16L) { 
     41    static if (n<16L) 
    3442        const char [] toHexString = hexdigit!(n); 
    35     } else { 
     43    else 
    3644        const char [] toHexString = toHexString!(n >> 4) ~ hexdigit!(n&0xF); 
    37     } 
    3845} 
    3946 
     
    4148 *  long atoi!(char [] s); 
    4249 */ 
    43 template atoi(char [] s, long sofar=0, int indx=0) 
    44 
    45   static if (s.length==indx) const long atoi=sofar; 
    46   else static if (indx==0 && s[indx]=='-') const long atoi = -.atoi!(s, 0, 1); 
    47   else static if (!isdigit!( (s[indx]) ) ) const long atoi = sofar; 
    48   else const long atoi = .atoi!(s, sofar * 10 + s[indx]-'0', indx+1); 
     50template atoi(char [] s, long result=0, int indx=0) 
     51
     52    static if (s.length==indx) 
     53        const long atoi = result; 
     54    else static if (indx==0 && s[indx]=='-') 
     55        const long atoi = - atoi!(s, 0, 1); 
     56    else static if (!isdigit!( (s[indx]) ) ) 
     57        const long atoi = result; 
     58    else  
     59        const long atoi = atoi!(s, result * 10 + s[indx]-'0', indx+1); 
    4960} 
    5061 
     
    5263 *  the number of characters in s[] which would be 'consumed' by atoi!(). 
    5364 */ 
    54 template countleadingdigits(char [] s, int indx=0) 
    55 
    56     static if (s.length==indx) { 
    57         const int countleadingdigits=indx; 
    58     } else static if (indx==0 && s[indx]=='-')  { 
    59         const int countleadingdigits = countleadingdigits!(s, indx+1); 
    60     } else static if (!isdigit!( (s[indx]) )) { 
    61         const int countleadingdigits=indx; 
    62     } else { 
    63         const int countleadingdigits = countleadingdigits!(s, indx+1); 
    64     } 
    65 
    66  
    67 /* ***************************** 
    68  *  int sigFigs!(char [] s) 
    69  * 
    70  * Returns the number of significant figures in the decimal number s. 
    71  * Leading zeros do not count towards the number of significant figures; 
    72  * eg, sigFigs!(0.000003) == 1. 
    73  *   sigFigs!(0.00000) == 0. 
    74  */ 
    75 template sigFigs(char [] s, int p=0, bool stillzero=true) 
    76 
    77     static if ( s.length ==p ) { 
    78         static if (p>0 && stillzero) { 
    79             const int sigFigs = 1; // the number was zero 
    80         } else { 
    81             const int sigFigs = 0; 
    82         } 
    83     } else static if ( s[p]=='.'|| (p==0 && s[p]=='-') ) { 
    84         const int sigFigs = sigFigs!(s, p+1, stillzero); 
    85     } else static if (s[p]=='0' && stillzero) { 
    86         const int sigFigs = sigFigs!(s, p+1, stillzero); 
    87     } else static if ( isdigit!((s[p])) ) { 
    88         const int sigFigs = 1 + sigFigs!(s, p+1, false); 
    89     } else static if ( s[p]=='e' || s[p]=='E' ) { 
    90         // abort once we reach the exponent 
    91         const int sigFigs=0; 
    92     } else { 
    93         static assert(0); // invalid character in string 
    94     } 
    95 
    96  
    97  
     65template atoiConsumed(char [] s, int indx=0) 
     66
     67    static if(s.length==indx) 
     68        const int atoiConsumed = indx; 
     69    else static if ((indx==0 && s[indx]=='-') || isdigit!( (s[indx]) ) ) 
     70        const int atoiConsumed = atoiConsumed!(s,indx + 1); 
     71    else    // invalid character 
     72        const int atoiConsumed = indx; 
     73
     74 
     75private enum  EFloatParseState { START=0, GOTSIGN, WAITDOT, WAITEXP=3, GOTEXPCHAR, GOTEXPSIGN, EXPDIG }; 
     76 
     77 
     78// Like atoi!(), except that internal underscores are allowable 
     79template parseInt(char [] s, long result=0, EFloatParseState state =0 /* start*/) 
     80
     81    static if (s.length==0)  
     82        const long parseInt = result; 
     83    else static if (state == EFloatParseState.START && s[0]=='-') 
     84        const long parseInt = - parseInt!(s[1..$], 0, GOTSIGN); 
     85    else static if (state == EFloatParseState.WAITDOT && s[0]=='_') 
     86        const long parseInt = parseInt!(s[1..$], result, state); 
     87    else static if (isdigit!( (s[0]) ) )  
     88        const long parseInt = parseInt!(s[1..$], result*10 + s[0]-'0', WAITDOT); 
     89
     90 
     91// Returns the value of the exponent. 
     92// must be of the form "" or  "e-543" or "E+2_453" etc  
     93template parseExponent(char [] s, int result = 0, EFloatParseState state = EFloatParseState.WAITEXP) 
     94
     95    static if (s.length == 0) { 
     96        // an empty string is acceptable 
     97        static if (state == EFloatParseState.WAITEXP || state == EFloatParseState.EXPDIG) 
     98            const int parseExponent = result; 
     99        else { 
     100            pragma(msg, "Error: No exponent found in floating-point literal."); 
     101            static assert(0); 
     102        } 
     103    } else static if (state == EFloatParseState.WAITEXP && (s[0]=='e' || s[0]=='E') ) 
     104        const int parseExponent = parseExponent!(s[1..$], 0, EFloatParseState.GOTEXPCHAR); 
     105    else static if (state == EFloatParseState.GOTEXPCHAR && s[0]=='-') 
     106        const int parseExponent = -parseExponent!(s[1..$], 0, EFloatParseState.GOTEXPSIGN); 
     107    else static if (state == EFloatParseState.GOTEXPCHAR && s[0]=='+') 
     108        const int parseExponent = parseExponent!(s[1..$], 0, EFloatParseState.GOTEXPSIGN); 
     109    else static if (state == EFloatParseState.EXPDIG && s[0]=='_') 
     110        // embedded underscores are allowed after the first digit 
     111        const int parseExponent = parseExponent!(s[1..$], result, state); 
     112    else static if (isdigit!( (s[0]) ) ) 
     113       const int parseExponent = parseExponent!(s[1..$], result*10 + (s[0]-'0'), EFloatParseState.EXPDIG);     
     114    else { 
     115        pragma(msg, "Error: Invalid characters found in floating-point literal."); 
     116        static assert(0); 
     117    } 
     118
     119 
     120// parse a %f-style floating-point number, returning the result as a real. 
     121// A minus sign is allowed as the first character. 
     122// Embedded underscores are allowed any time after the first digit. 
     123template parseMantissa(char [] s, EFloatParseState state = EFloatParseState.START, real result=0.0L) 
     124
     125    static if (s.length==0)  
     126        const real parseMantissa = result; 
     127    else static if (state==EFloatParseState.START && s[0]=='-') 
     128        const real parseMantissa = - parseMantissa!(s[1..$], EFloatParseState.GOTSIGN, 0.0L); 
     129    else static if (state==EFloatParseState.WAITDOT && s[0]=='.') 
     130        const real parseMantissa = result + parseMantissa!(s[1..$], EFloatParseState.WAITEXP, 0.0L); 
     131    else static if (state!=EFloatParseState.START  && state!=EFloatParseState.GOTSIGN && s[0]=='_') 
     132        // allow embedded underscores, but not before the first digit 
     133        const real parseMantissa = parseMantissa!(s[1..$], state, result); 
     134    else static if (isdigit!( (s[0]) )) { 
     135        static if (state == EFloatParseState.WAITEXP) 
     136            // fractional part 
     137            const real parseMantissa = result + (s[0]-'0')/10.0L + parseMantissa!(s[1..$], state, 0.0L) /10.0L; 
     138        else 
     139            const real parseMantissa = parseMantissa!(s[1..$], EFloatParseState.WAITDOT, result*10.0L + (s[0]-'0') ); 
     140    } else static if (state == EFloatParseState.WAITDOT || state == EFloatParseState.WAITEXP) 
     141            const real parseMantissa = result; 
     142    else { 
     143        pragma(msg, "Error: No digits found in floating-point literal."); 
     144        static assert(0); 
     145    } 
     146
     147 
     148template parseMantissaConsumed(char [] s, EFloatParseState state = EFloatParseState.START) 
     149
     150    static if (s.length == 0) 
     151        const int parseMantissaConsumed = 0; 
     152    else static if (state==EFloatParseState.START && s[0]=='-') 
     153        const int parseMantissaConsumed = 1 + parseMantissaConsumed!(s[1..$], EFloatParseState.GOTSIGN); 
     154    else static if (state==EFloatParseState.WAITDOT && s[0]=='.') 
     155        const int parseMantissaConsumed = 1 + parseMantissaConsumed!(s[1..$], EFloatParseState.WAITEXP); 
     156    else static if (state!=EFloatParseState.START  && state!=EFloatParseState.GOTSIGN && s[0]=='_') 
     157        // allow embedded underscores, but not before the first digit 
     158        const int parseMantissaConsumed = 1 + parseMantissaConsumed!(s[1..$], state); 
     159    else static if (isdigit!( (s[0]) )) 
     160        const int parseMantissaConsumed = 1 + parseMantissaConsumed!(s[1..$], 
     161            (state == EFloatParseState.WAITEXP) ? state : EFloatParseState.WAITDOT ); 
     162    else // found first offending character.  
     163        //static assert(state != EFloatParseState.START && state!= EFloatParseState.GOTSIGN); 
     164        const int parseMantissaConsumed = 0; 
     165
     166 
     167 
     168// accepts %f or %e format. 
     169template atof(char [] s) 
     170
     171    const real atof = parseMantissa!(s) * meta.math.pow!(10.0L,  
     172            parseExponent!(s[parseMantissaConsumed!(s)..$])); 
     173
     174 
     175// returns number of decimal places in s. 
     176// s must be a valid floating-point literal 
     177template decimalplaces(char [] s, bool gotDot = false) 
     178
     179    static if (s.length==0) 
     180        const int decimalplaces = 0; 
     181    else static if ( !gotDot && s[0]=='.') 
     182        const int decimalplaces = decimalplaces!(s[1..$], true); 
     183    else static if (s[0]=='_' || !gotDot) { 
     184        const int decimalplaces = decimalplaces!(s[1..$], gotDot); 
     185    } else { 
     186        static if (isdigit!( (s[0]) ) ) 
     187            const int decimalplaces = 1 + decimalplaces!(s[1..$], true); 
     188        else // we've finished 
     189            const int decimalplaces = 0; 
     190    } 
     191
     192 
     193 
     194//------------------------------------------------ 
    98195// Given a number x, where 0<= x <1, 
    99196// returns the first 'maxdigs' digits after the decimal point. 
    100197template afterdec(real x, int maxdigs=real.dig) 
    101198{ 
    102   static if (maxdigs==0 || x==0) const char [] afterdec = ""; 
     199    static if (maxdigs==0 || x==0) const char [] afterdec = ""; 
    103200  else const char [] afterdec = decimaldigit!(cast(int)(x*10)) ~ afterdec!(x*10-cast(int)(x*10), maxdigs-1); 
    104201} 
     
    138235 
    139236version(testmeta) { 
    140  private import meta.strhacks; 
    141  
    142  static assert( streq!(pcvt!(0x1.12345p954L), "0x1.123450p+954") ); 
    143  static assert( streq!(fcvt!(12.345), "12.345") ); 
     237 
     238 
     239    static assert(parseMantissa!("3.34") == 3.34); 
     240    static assert(parseMantissa!("-548_29.317_1abc") == -548_29.317_1); 
     241    static assert( isPositiveZero!(parseMantissa!("0.0"))); 
     242    static assert( isNegativeZero!(parseMantissa!("-0.0"))); 
     243    static assert(parseMantissaConsumed!("-31_4.3252e34") == 10); 
     244    static assert(parseMantissaConsumed!("_23e112") == 0); 
     245 
     246 static assert( pcvt!(0x1.12345p954L) == "0x1.123450p+954" ); 
     247 static assert( fcvt!(12.345) == "12.345" ); 
    144248 
    145249 static assert( atoi!("3580abc")==3580); 
    146250 static assert( atoi!("-0326")==-326); 
    147  static assert( countleadingdigits!("325827wip")==6); 
    148  static assert( countleadingdigits!("abc")==0); 
    149  static assert( sigFigs!("1.2500")==5); 
    150  static assert( sigFigs!("-380.0")==4); 
    151  static assert( sigFigs!("0")==1); 
    152  static assert( sigFigs!("0.0025")==2); 
    153  static assert( sigFigs!("-1.20e49")==3); 
    154 
     251 static assert( atoiConsumed!("325827wip")==6); 
     252 static assert( atoiConsumed!("abc")==0); 
     253