Skip to content

Commit 4ea1cf0

Browse files
committed
Implemented a parser and updated tiny_obj_loader.cc to use it unless a define is set.
1 parent f020169 commit 4ea1cf0

2 files changed

Lines changed: 341 additions & 12 deletions

File tree

‎float_parser_tests.cc‎

Lines changed: 188 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,159 @@
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

32150
void 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\tExpected 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

Comments
 (0)