Skip to content

Commit c06ea7a

Browse files
committed
py: Implement parsing of infinity and nan for floats.
1 parent 1609f85 commit c06ea7a

4 files changed

Lines changed: 105 additions & 45 deletions

File tree

py/objfloat.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ STATIC mp_obj_t float_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const m
3838
// a string, parse it
3939
uint l;
4040
const char *s = mp_obj_str_get_data(args[0], &l);
41-
return mp_parse_num_decimal(s, l);
41+
return mp_parse_num_decimal(s, l, false);
4242
} else if (MP_OBJ_IS_TYPE(args[0], &mp_type_float)) {
4343
return args[0];
4444
} else {

py/parsenum.c

Lines changed: 102 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
#include "parsenumbase.h"
1010
#include "parsenum.h"
1111

12+
#if MICROPY_ENABLE_FLOAT
13+
#include <math.h>
14+
#endif
15+
1216
#if defined(UNIX)
1317

1418
#include <ctype.h>
@@ -84,64 +88,120 @@ mp_obj_t mp_parse_num_integer(const char *restrict str, uint len, int base) {
8488
#define PARSE_DEC_IN_FRAC (2)
8589
#define PARSE_DEC_IN_EXP (3)
8690

87-
mp_obj_t mp_parse_num_decimal(const char *str, uint len) {
91+
mp_obj_t mp_parse_num_decimal(const char *str, uint len, bool allow_imag) {
8892
#if MICROPY_ENABLE_FLOAT
89-
int in = PARSE_DEC_IN_INTG;
93+
const char *top = str + len;
9094
mp_float_t dec_val = 0;
91-
bool exp_neg = false;
92-
int exp_val = 0;
93-
int exp_extra = 0;
95+
bool dec_neg = false;
9496
bool imag = false;
95-
const char *top = str + len;
96-
for (; str < top; str++) {
97-
int dig = *str;
98-
if ('0' <= dig && dig <= '9') {
99-
dig -= '0';
100-
if (in == PARSE_DEC_IN_EXP) {
101-
exp_val = 10 * exp_val + dig;
102-
} else {
103-
dec_val = 10 * dec_val + dig;
104-
if (in == PARSE_DEC_IN_FRAC) {
105-
exp_extra -= 1;
106-
}
97+
98+
// skip leading space
99+
for (; str < top && isspace(*str); str++) {
100+
}
101+
102+
// get optional sign
103+
if (str < top) {
104+
if (*str == '+') {
105+
str++;
106+
} else if (*str == '-') {
107+
str++;
108+
dec_neg = true;
109+
}
110+
}
111+
112+
// determine what the string is
113+
if (str < top && (str[0] | 0x20) == 'i') {
114+
// string starts with 'i', should be 'inf' or 'infinity' (case insensitive)
115+
if (str + 2 < top && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'f') {
116+
// inf
117+
str += 3;
118+
dec_val = INFINITY;
119+
if (str + 4 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'i' && (str[3] | 0x20) == 't' && (str[4] | 0x20) == 'y') {
120+
// infinity
121+
str += 5;
107122
}
108-
} else if (in == PARSE_DEC_IN_INTG && dig == '.') {
109-
in = PARSE_DEC_IN_FRAC;
110-
} else if (in != PARSE_DEC_IN_EXP && (dig == 'E' || dig == 'e')) {
111-
in = PARSE_DEC_IN_EXP;
112-
if (str[1] == '+') {
113-
str++;
114-
} else if (str[1] == '-') {
123+
}
124+
} else if (str < top && (str[0] | 0x20) == 'n') {
125+
// string starts with 'n', should be 'nan' (case insensitive)
126+
if (str + 2 < top && (str[1] | 0x20) == 'a' && (str[2] | 0x20) == 'n') {
127+
// NaN
128+
str += 3;
129+
dec_val = MICROPY_FLOAT_C_FUN(nan)("");
130+
}
131+
} else {
132+
// parse the digits
133+
int in = PARSE_DEC_IN_INTG;
134+
bool exp_neg = false;
135+
int exp_val = 0;
136+
int exp_extra = 0;
137+
for (; str < top; str++) {
138+
int dig = *str;
139+
if ('0' <= dig && dig <= '9') {
140+
dig -= '0';
141+
if (in == PARSE_DEC_IN_EXP) {
142+
exp_val = 10 * exp_val + dig;
143+
} else {
144+
dec_val = 10 * dec_val + dig;
145+
if (in == PARSE_DEC_IN_FRAC) {
146+
exp_extra -= 1;
147+
}
148+
}
149+
} else if (in == PARSE_DEC_IN_INTG && dig == '.') {
150+
in = PARSE_DEC_IN_FRAC;
151+
} else if (in != PARSE_DEC_IN_EXP && ((dig | 0x20) == 'e')) {
152+
in = PARSE_DEC_IN_EXP;
153+
if (str[1] == '+') {
154+
str++;
155+
} else if (str[1] == '-') {
156+
str++;
157+
exp_neg = true;
158+
}
159+
} else if (allow_imag && (dig | 0x20) == 'j') {
115160
str++;
116-
exp_neg = true;
161+
imag = true;
162+
break;
163+
} else {
164+
// unknown character
165+
break;
117166
}
118-
} else if (dig == 'J' || dig == 'j') {
119-
str++;
120-
imag = true;
121-
break;
122-
} else {
123-
// unknown character
124-
break;
167+
}
168+
169+
// work out the exponent
170+
if (exp_neg) {
171+
exp_val = -exp_val;
172+
}
173+
exp_val += exp_extra;
174+
175+
// apply the exponent
176+
for (; exp_val > 0; exp_val--) {
177+
dec_val *= 10;
178+
}
179+
for (; exp_val < 0; exp_val++) {
180+
dec_val *= 0.1;
125181
}
126182
}
127-
if (*str != 0) {
128-
nlr_jump(mp_obj_new_exception_msg(&mp_type_SyntaxError, "invalid syntax for number"));
129-
}
130-
if (exp_neg) {
131-
exp_val = -exp_val;
183+
184+
// negate value if needed
185+
if (dec_neg) {
186+
dec_val = -dec_val;
132187
}
133-
exp_val += exp_extra;
134-
for (; exp_val > 0; exp_val--) {
135-
dec_val *= 10;
188+
189+
// skip trailing space
190+
for (; str < top && isspace(*str); str++) {
136191
}
137-
for (; exp_val < 0; exp_val++) {
138-
dec_val *= 0.1;
192+
193+
// check we reached the end of the string
194+
if (str != top) {
195+
nlr_jump(mp_obj_new_exception_msg(&mp_type_SyntaxError, "invalid syntax for number"));
139196
}
197+
198+
// return the object
140199
if (imag) {
141200
return mp_obj_new_complex(0, dec_val);
142201
} else {
143202
return mp_obj_new_float(dec_val);
144203
}
204+
145205
#else
146206
nlr_jump(mp_obj_new_exception_msg(&mp_type_SyntaxError, "decimal numbers not supported"));
147207
#endif

py/parsenum.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
mp_obj_t mp_parse_num_integer(const char *restrict str, uint len, int base);
2-
mp_obj_t mp_parse_num_decimal(const char *str, uint len);
2+
mp_obj_t mp_parse_num_decimal(const char *str, uint len, bool allow_imag);

py/runtime.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ mp_obj_t rt_load_const_dec(qstr qstr) {
375375
DEBUG_OP_printf("load '%s'\n", qstr_str(qstr));
376376
uint len;
377377
const byte* data = qstr_data(qstr, &len);
378-
return mp_parse_num_decimal((const char*)data, len);
378+
return mp_parse_num_decimal((const char*)data, len, true);
379379
}
380380

381381
mp_obj_t rt_load_const_str(qstr qstr) {

0 commit comments

Comments
 (0)