11#include < cstdio>
22#include < cstddef>
3+ #include < cstring>
4+ #include < cmath>
5+ #include < cctype>
36
47// Tries to parse a floating point number located at s.
58//
9+ // s_end should be a location in the string where reading should absolutely
10+ // stop. For example at the end of the string, to prevent buffer overflows.
11+ //
612// Parses the following EBNF grammar:
713// sign = "+" | "-" ;
814// END = ? anything not in digit ?
915// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
1016// integer = [sign] , digit , {digit} ;
11- // decimal = integer , ["." , {digit} ] ;
12- // float = ( decimal , END ) | ( decimal , ("E" | "e") , decimal , END ) ;
17+ // decimal = integer , ["." , integer ] ;
18+ // float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ;
1319//
1420// Valid strings are for example:
15- // -0 +3.1417e+2 -0.E -3 1.0324 -1.41 11e2
21+ // -0 +3.1417e+2 -0.0E -3 1.0324 -1.41 11e2
1622//
1723// If the parsing is a success, result is set to the parsed value and true
1824// is returned.
1925//
20- // The function is greedy, it will parse until it encounters a null-byte or
21- // a non-conforming character is encountered.
26+ // The function is greedy and will parse until any of the following happens:
27+ // - a non-conforming character is encountered.
28+ // - s_end is reached.
2229//
2330// The following situations triggers a failure:
31+ // - s >= s_end.
2432// - parse failure.
25- // - underflow/overflow.
2633//
27- bool tryParseFloat (const char *s, double *result)
34+ bool tryParseDouble (const char *s, const char *s_end , double *result)
2835{
36+ if (s >= s_end)
37+ {
38+ return false ;
39+ }
40+
41+ double mantissa = 0.0 ;
42+ // This exponent is base 2 rather than 10.
43+ // However the exponent we parse is supposed to be one of ten,
44+ // thus we must take care to convert the exponent/and or the
45+ // mantissa to a * 2^E, where a is the mantissa and E is the
46+ // exponent.
47+ // To get the final double we will use ldexp, it requires the
48+ // exponent to be in base 2.
49+ int exponent = 0 ;
50+
51+ // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED
52+ // TO JUMP OVER DEFINITIONS.
53+ char sign = ' +' ;
54+ char exp_sign = ' +' ;
55+ char const *curr = s;
56+
57+ // How many characters were read in a loop.
58+ int read = 0 ;
59+ // Tells whether a loop terminated due to reaching s_end.
60+ bool end_not_reached = false ;
61+
62+ /*
63+ BEGIN PARSING.
64+ */
65+
66+ // Find out what sign we've got.
67+ if (*curr == ' +' || *curr == ' -' )
68+ {
69+ sign = *curr;
70+ curr++;
71+ }
72+ else if (isdigit (*curr)) { /* Pass through. */ }
73+ else
74+ {
75+ goto fail;
76+ }
77+
78+ // Read the integer part.
79+ while ((end_not_reached = (curr != s_end)) && isdigit (*curr))
80+ {
81+ mantissa *= 10 ;
82+ mantissa += static_cast <int >(*curr - 0x30 );
83+ curr++; read++;
84+ }
85+
86+ // We must make sure we actually got something.
87+ if (read == 0 )
88+ goto fail;
89+ // We allow numbers of form "#", "###" etc.
90+ if (!end_not_reached)
91+ goto assemble;
92+
93+ // Read the decimal part.
94+ if (*curr == ' .' )
95+ {
96+ curr++;
97+ read = 1 ;
98+ while ((end_not_reached = (curr != s_end)) && isdigit (*curr))
99+ {
100+ // NOTE: Don't use powf here, it will absolutely murder precision.
101+ mantissa += static_cast <int >(*curr - 0x30 ) * pow (10 , -read);
102+ read++; curr++;
103+ }
104+ }
105+ else if (*curr == ' e' || *curr == ' E' ) {}
106+ else
107+ {
108+ goto assemble;
109+ }
110+
111+ if (!end_not_reached)
112+ goto assemble;
113+
114+ // Read the exponent part.
115+ if (*curr == ' e' || *curr == ' E' )
116+ {
117+ curr++;
118+ // Figure out if a sign is present and if it is.
119+ if ((end_not_reached = (curr != s_end)) && (*curr == ' +' || *curr == ' -' ))
120+ {
121+ exp_sign = *curr;
122+ curr++;
123+ }
124+ else if (isdigit (*curr)) { /* Pass through. */ }
125+ else
126+ {
127+ // Empty E is not allowed.
128+ goto fail;
129+ }
130+
131+ read = 0 ;
132+ while ((end_not_reached = (curr != s_end)) && isdigit (*curr))
133+ {
134+ exponent *= 10 ;
135+ exponent += static_cast <int >(*curr - 0x30 );
136+ curr++; read++;
137+ }
138+ exponent *= (exp_sign == ' +' ? 1 : -1 );
139+ if (read == 0 )
140+ goto fail;
141+ }
142+
143+ assemble:
144+ *result = (sign == ' +' ? 1 : -1 ) * ldexp (mantissa * pow (5 , exponent), exponent);
145+ return true ;
146+ fail:
29147 return false ;
30148}
31149
32150void testParsing (const char *input, bool expectedRes, double expectedValue)
33151{
34152 double val = 0.0 ;
35- bool res = tryParseFloat (input, &val);
36- if (res != expectedRes || val != expectedValue)
153+ bool res = tryParseDouble (input, input + strlen (input) , &val);
154+ if (res != expectedRes || fabs ( val - expectedValue) > 0.000001 )
37155 {
38- printf (" % 20s failed, returned %d and value % 10.4f .\n " , input, res, val);
156+ printf (" % 20s failed, returned %d and value % 10.5f .\n\t\t Expected value was % 10.5f \n ------ \n " , input, res, val, expectedValue );
39157 }
40158}
41159
@@ -49,7 +167,65 @@ int main(int argc, char const *argv[])
49167 testParsing (" -1" , true , -1.0 );
50168 testParsing (" -1.08" , true , -1.08 );
51169 testParsing (" 100.0823blabla" , true , 100.0823 );
52- testParsing (" 34.E-2\04 " , true , 34.0e-2 );
53- testParsing (" +34.23E2\02 " , true , 34.23E2 );
170+ testParsing (" 34.0E-2\04 " , true , 34.0e-2 );
171+ testParsing (" +34.23E2\032 " , true , 34.23E2 );
172+ testParsing (" 10.023" , true , 10.023 );
173+ testParsing (" 1.0e+23" , true , 1 .e +23 );
174+ testParsing (" 10e+23" , true , 10e+23 );
175+ testParsing (" 20301.000" , true , 20301.000 );
176+ testParsing (" -41044.000" , true , -41044.000 );
177+ testParsing (" -93558.000" , true , -93558.000 );
178+ testParsing (" -79620.000" , true , -79620.000 );
179+ testParsing (" 5257.000" , true , 5257.000 );
180+ testParsing (" -7145.000" , true , -7145.000 );
181+ testParsing (" -77314.000" , true , -77314.000 );
182+ testParsing (" -27131.000" , true , -27131.000 );
183+ testParsing (" 52744.000" , true , 52744.000 );
184+ testParsing (" 48106.000" , true , 48106.000 );
185+ testParsing (" 42939.000" , true , 42939.000 );
186+ testParsing (" 41187.000" , true , 41187.000 );
187+ testParsing (" 70851.000" , true , 70851.000 );
188+ testParsing (" -90431.000" , true , -90431.000 );
189+ testParsing (" -13564.000" , true , -13564.000 );
190+ testParsing (" 27150.000" , true , 27150.000 );
191+ testParsing (" 71992.000" , true , 71992.000 );
192+ testParsing (" -42845.000" , true , -42845.000 );
193+ testParsing (" 24806.000" , true , 24806.000 );
194+ testParsing (" 30753.000" , true , 30753.000 );
195+ testParsing (" 1.658500e+04" , true , 1.658500e+04 );
196+ testParsing (" 9.572500e+04" , true , 9.572500e+04 );
197+ testParsing (" -5.888600e+04" , true , -5.888600e+04 );
198+ testParsing (" -2.998000e+04" , true , -2.998000e+04 );
199+ testParsing (" -4.842700e+04" , true , -4.842700e+04 );
200+ testParsing (" 9.575400e+04" , true , 9.575400e+04 );
201+ testParsing (" -8.311500e+04" , true , -8.311500e+04 );
202+ testParsing (" -3.770800e+04" , true , -3.770800e+04 );
203+ testParsing (" -3.141600e+04" , true , -3.141600e+04 );
204+ testParsing (" -4.673700e+04" , true , -4.673700e+04 );
205+ testParsing (" -5.112400e+04" , true , -5.112400e+04 );
206+ testParsing (" 7.111000e+03" , true , 7.111000e+03 );
207+ testParsing (" -3.727000e+03" , true , -3.727000e+03 );
208+ testParsing (" 6.940700e+04" , true , 6.940700e+04 );
209+ testParsing (" -3.635100e+04" , true , -3.635100e+04 );
210+ testParsing (" 2.762100e+04" , true , 2.762100e+04 );
211+ testParsing (" -1.512600e+04" , true , -1.512600e+04 );
212+ testParsing (" 3.338000e+03" , true , 3.338000e+03 );
213+ testParsing (" 7.598100e+04" , true , 7.598100e+04 );
214+ testParsing (" -8.198400e+04" , true , -8.198400e+04 );
215+ testParsing (" -" , false , 0.0 );
216+ testParsing (" +" , false , 0.0 );
217+ testParsing (" " , false , 0.0 );
218+ testParsing (" .232" , false , 0.0 );
219+ testParsing (" .232E2" , false , 0.0 );
220+ testParsing (" .232" , false , 0.0 );
221+ testParsing (" ." , false , 0.0 );
222+ testParsing (" .E" , false , 0.0 );
223+ testParsing (" 233.E" , false , 0.0 );
224+ testParsing (" 233.323E-" , false , 0.0 );
225+ testParsing (" 233.323E+" , false , 0.0 );
226+
227+
228+
229+
54230 return 0 ;
55231}
0 commit comments