diff options
author | Damien George <damien.p.george@gmail.com> | 2017-11-27 12:51:52 +1100 |
---|---|---|
committer | Damien George <damien.p.george@gmail.com> | 2017-11-27 12:51:52 +1100 |
commit | 84895f1a210d0037a86887f0f647570bdf40afa2 (patch) | |
tree | d7f1266b5849f55c70e6df87a0a73c22041ec9e4 /py/parsenum.c | |
parent | f59c6b48aed765fc0eb3785686ffb11f2efc8eae (diff) | |
download | micropython-84895f1a210d0037a86887f0f647570bdf40afa2.tar.gz micropython-84895f1a210d0037a86887f0f647570bdf40afa2.zip |
py/parsenum: Improve parsing of floating point numbers.
This patch improves parsing of floating point numbers by converting all the
digits (integer and fractional) together into a number 1 or greater, and
then applying the correct power of 10 at the very end. In particular the
multiple "multiply by 0.1" operations to build a fraction are now combined
together and applied at the same time as the exponent, at the very end.
This helps to retain precision during parsing of floats, and also includes
a check that the number doesn't overflow during the parsing. One benefit
is that a float will have the same value no matter where the decimal point
is located, eg 1.23 == 123e-2.
Diffstat (limited to 'py/parsenum.c')
-rw-r--r-- | py/parsenum.c | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/py/parsenum.c b/py/parsenum.c index b62029f7c7..98e7736851 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -170,6 +170,14 @@ typedef enum { mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex) { #if MICROPY_PY_BUILTINS_FLOAT + +// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define DEC_VAL_MAX 1e20F +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define DEC_VAL_MAX 1e200 +#endif + const char *top = str + len; mp_float_t dec_val = 0; bool dec_neg = false; @@ -214,8 +222,8 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool // string should be a decimal number parse_dec_in_t in = PARSE_DEC_IN_INTG; bool exp_neg = false; - mp_float_t frac_mult = 0.1; mp_int_t exp_val = 0; + mp_int_t exp_extra = 0; while (str < top) { mp_uint_t dig = *str++; if ('0' <= dig && dig <= '9') { @@ -223,11 +231,18 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool if (in == PARSE_DEC_IN_EXP) { exp_val = 10 * exp_val + dig; } else { - if (in == PARSE_DEC_IN_FRAC) { - dec_val += dig * frac_mult; - frac_mult *= MICROPY_FLOAT_CONST(0.1); - } else { + if (dec_val < DEC_VAL_MAX) { + // dec_val won't overflow so keep accumulating dec_val = 10 * dec_val + dig; + if (in == PARSE_DEC_IN_FRAC) { + --exp_extra; + } + } else { + // dec_val might overflow and we anyway can't represent more digits + // of precision, so ignore the digit and just adjust the exponent + if (in == PARSE_DEC_IN_INTG) { + ++exp_extra; + } } } } else if (in == PARSE_DEC_IN_INTG && dig == '.') { @@ -261,7 +276,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool } // apply the exponent - dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val); + dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val + exp_extra); } // negate value if needed |