8000 py/parsenum: Ensure that trailing zeros lead to identical results. · micropython/micropython@6cd2e41 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6cd2e41

Browse files
dpwedpgeorge
authored andcommitted
py/parsenum: Ensure that trailing zeros lead to identical results.
Prior to this commit, parsenum would calculate "1e-20" as 1.0*pow(10, -20), and "1.000e-20" as 1000.0*pow(10, -23); in certain cases, this could make seemingly-identical values compare as not equal. This commit watches for trailing zeros as a special case, and ignores them when appropriate, so "1.000e-20" is also calculated as 1.0*pow(10, -20). Fixes issue #5831.
1 parent 6971992 commit 6cd2e41

File tree

1 file changed

+49
-21
lines changed

1 file changed

+49
-21
lines changed

py/parsenum.c

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -178,31 +178,51 @@ typedef enum {
178178
PARSE_DEC_IN_EXP,
179179
} parse_dec_in_t;
180180

181-
#if MICROPY_PY_BUILTINS_COMPLEX
182-
mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex)
183-
#else
184-
mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex)
185-
#endif
186-
{
187-
#if MICROPY_PY_BUILTINS_FLOAT
188-
181+
#if MICROPY_PY_BUILTINS_FLOAT
189182
// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing
190183
// SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float
191184
// EXACT_POWER_OF_10 is the largest value of x so that 10^x can be stored exactly in a float
192185
// Note: EXACT_POWER_OF_10 is at least floor(log_5(2^mantissa_length)). Indeed, 10^n = 2^n * 5^n
193186
// so we only have to store the 5^n part in the mantissa (the 2^n part will go into the float's
194187
// exponent).
195-
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
188+
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
196189
#define DEC_VAL_MAX 1e20F
197190
#define SMALL_NORMAL_VAL (1e-37F)
198191
#define SMALL_NORMAL_EXP (-37)
199192
#define EXACT_POWER_OF_10 (9)
200-
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
193+
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
201194
#define DEC_VAL_MAX 1e200
202195
#define SMALL_NORMAL_VAL (1e-307)
203196
#define SMALL_NORMAL_EXP (-307)
204197
#define EXACT_POWER_OF_10 (22)
205-
#endif
198+
#endif
199+
200+
// Break out inner digit accumulation routine to ease trailing zero deferral.
201+
static void accept_digit(mp_float_t *p_dec_val, int dig, int *p_exp_extra, int in) {
202+
// Core routine to ingest an additional digit.
203+
if (*p_dec_val < DEC_VAL_MAX) {
204+
// dec_val won't overflow so keep accumulating
205+
*p_dec_val = 10 * *p_dec_val + dig;
206+
if (in == PARSE_DEC_IN_FRAC) {
207+
--(*p_exp_extra);
208+
}
209+
} else {
210+
// dec_val might overflow and we anyway can't represent more digits
211+
// of precision, so ignore the digit and just adjust the exponent
212+
if (in == PARSE_DEC_IN_INTG) {
213+
++(*p_exp_extra);
214+
}
215+
}
216+
}
217+
#endif // MICROPY_BUILTINS_FLOAT
218+
219+
#if MICROPY_PY_BUILTINS_COMPLEX
220+
mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex)
221+
#else
222+
mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex)
223+
#endif
224+
{
225+
#if MICROPY_PY_BUILTINS_FLOAT
206226

207227
const char *top = str + len;
208228
mp_float_t dec_val = 0;
@@ -255,6 +275,7 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
255275
bool exp_neg = false;
256276
int exp_val = 0;
257277
int exp_extra = 0;
278+
int trailing_zeros_intg = 0, trailing_zeros_frac = 0;
258279
while (str < top) {
259280
unsigned int dig = *str++;
260281
if ('0' <= dig && dig <= '9') {
@@ -267,18 +288,25 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
267288
exp_val = 10 * exp_val + dig;
268289
}
269290
} else {
270-
if (dec_val < DEC_VAL_MAX) {
271-
// dec_val won't overflow so keep accumulating
272-
dec_val = 10 * dec_val + dig;
273-
if (in == PARSE_DEC_IN_FRAC) {
274-
--exp_extra;
291+
if (dig == 0 || dec_val >= DEC_VAL_MAX) {
292+
// Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them.
293+
// Also, once we reach DEC_VAL_MAX, treat every additional digit as a trailing zero.
294+
if (in == PARSE_DEC_IN_INTG) {
295+
++trailing_zeros_intg;
296+
} else {
297+
++trailing_zeros_frac;
275298
}
276299
} else {
277-
// dec_val might overflow and we anyway can't represent more digits
278-
// of precision, so ignore the digit and just adjust the exponent
279-
if (in == PARSE_DEC_IN_INTG) {
280-
++exp_extra;
300+
// Time to un-defer any trailing zeros. Intg zeros first.
301+
while (trailing_zeros_intg) {
302+
accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_INTG);
303+
--trailing_zeros_intg;
304+
}
305+
while (trailing_zeros_frac) {
306+
accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_FRAC);
307+
--trailing_zeros_frac;
281308
}
309+
accept_digit(&dec_val, dig, &exp_extra, in);
282310
}
283311
}
284312
} else if (in == PARSE_DEC_IN_INTG && dig == '.') {
@@ -311,7 +339,7 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
311339
}
312340

313341
// apply the exponent, making sure it's not a subnormal value
314-
exp_val += exp_extra;
342+
exp_val += exp_extra + trailing_zeros_intg;
315343
if (exp_val < SMALL_NORMAL_EXP) {
316344
exp_val -= SMALL_NORMAL_EXP;
317345
dec_val *= SMALL_NORMAL_VAL;

0 commit comments

Comments
 (0)
0