From a6e4c7a47a05320961eb46dd674aeee12ba92913 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 28 Mar 2020 16:43:15 -0500 Subject: [PATCH] py/parsenum: fix rounding error when float ends in .0 It is common code style to write floating point numbers with trailing `.0` instead of just trailing `.`. When there is a trailing `.0`, the current parser will multiply the number by 10 and then divide it by 10 again later. For certain numbers, this can cause rounding. This adds a check for the special case of `.0` to avoid the extra work. Also it avoids a call to pow() and extra multiplication when the exp_val is 0. Even in cases where there is not a rounding problem, this should be more efficient, especially for soft float implementations. --- py/parsenum.c | 7 ++++++- tests/float/float_parse.py | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/py/parsenum.c b/py/parsenum.c index cdcae8081f5ef..e5c358b9ddc8b 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -265,6 +265,11 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool } } else if (in == PARSE_DEC_IN_INTG && dig == '.') { in = PARSE_DEC_IN_FRAC; + // check for case of single '0' after decimal to avoid rounding error + if (str + 1 == top && *str == '0') { + str++; + break; + } } else if (in != PARSE_DEC_IN_EXP && ((dig | 0x20) == 'e')) { in = PARSE_DEC_IN_EXP; if (str < top) { @@ -309,7 +314,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool // of slightly erroneous values. if (exp_val < 0 && exp_val >= -EXACT_POWER_OF_10) { dec_val /= MICROPY_FLOAT_C_FUN(pow)(10, -exp_val); - } else { + } else if (exp_val != 0) { dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val); } } diff --git a/tests/float/float_parse.py b/tests/float/float_parse.py index 4b5fc613d314d..6ad638c994474 100644 --- a/tests/float/float_parse.py +++ b/tests/float/float_parse.py @@ -34,3 +34,8 @@ # check small decimals are as close to their true value as possible for n in range(1, 10): print(float('0.%u' % n) == n / 10) + +# Issue #5831 - make sure trailing 0 does not cause rounding with 32-bit float +# 14187745 has exact 32-bit representation but 141877450 does not +print(int(float("14187745."))) +print(int(float("14187745.0")))