Skip to content

Commit c8e8da6

Browse files
committed
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. A further check is added to avoid calling pow() when exp_val is 0. Even in cases where there is not a rounding problem, this should be more efficient, especially for soft float implementations.
1 parent 6883233 commit c8e8da6

File tree

2 files changed

+11
-1
lines changed

2 files changed

+11
-1
lines changed

py/parsenum.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,11 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
265265
}
266266
} else if (in == PARSE_DEC_IN_INTG && dig == '.') {
267267
in = PARSE_DEC_IN_FRAC;
268+
// check for case of single '0' after decimal to avoid rounding error
269+
if (str + 1 == top && *str == '0') {
270+
str++;
271+
break;
272+
}
268273
} else if (in != PARSE_DEC_IN_EXP && ((dig | 0x20) == 'e')) {
269274
in = PARSE_DEC_IN_EXP;
270275
if (str < top) {
@@ -309,7 +314,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
309314
// of slightly erroneous values.
310315
if (exp_val < 0 && exp_val >= -EXACT_POWER_OF_10) {
311316
dec_val /= MICROPY_FLOAT_C_FUN(pow)(10, -exp_val);
312-
} else {
317+
} else if (exp_val > 0) {
313318
dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val);
314319
}
315320
}

tests/float/float_parse.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,8 @@
3434
# check small decimals are as close to their true value as possible
3535
for n in range(1, 10):
3636
print(float('0.%u' % n) == n / 10)
37+
38+
# Issue #5831 - make sure trailing 0 does not cause rounding with 32-bit float
39+
# 14187745 has exact 32-bit representation but 141877450 does not
40+
print(int(float("14187745.")))
41+
print(int(float("14187745.0")))

0 commit comments

Comments
 (0)