| 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 | | |
|---|
| | 65 | template 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 | |
|---|
| | 75 | private enum EFloatParseState { START=0, GOTSIGN, WAITDOT, WAITEXP=3, GOTEXPCHAR, GOTEXPSIGN, EXPDIG }; |
|---|
| | 76 | |
|---|
| | 77 | |
|---|
| | 78 | // Like atoi!(), except that internal underscores are allowable |
|---|
| | 79 | template 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 |
|---|
| | 93 | template 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. |
|---|
| | 123 | template 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 | |
|---|
| | 148 | template 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. |
|---|
| | 169 | template 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 |
|---|
| | 177 | template 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 | //------------------------------------------------ |
|---|