summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--py/parsenum.c14
-rw-r--r--tests/float/float_parse.py5
-rw-r--r--tests/float/float_parse_doubleprec.py5
3 files changed, 22 insertions, 2 deletions
diff --git a/py/parsenum.c b/py/parsenum.c
index 98e773685..124489c66 100644
--- a/py/parsenum.c
+++ b/py/parsenum.c
@@ -172,10 +172,15 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
#if MICROPY_PY_BUILTINS_FLOAT
// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing
+// SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
#define DEC_VAL_MAX 1e20F
+#define SMALL_NORMAL_VAL (1e-37F)
+#define SMALL_NORMAL_EXP (-37)
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
#define DEC_VAL_MAX 1e200
+#define SMALL_NORMAL_VAL (1e-307)
+#define SMALL_NORMAL_EXP (-307)
#endif
const char *top = str + len;
@@ -275,8 +280,13 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
exp_val = -exp_val;
}
- // apply the exponent
- dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val + exp_extra);
+ // apply the exponent, making sure it's not a subnormal value
+ exp_val += exp_extra;
+ if (exp_val < SMALL_NORMAL_EXP) {
+ exp_val -= SMALL_NORMAL_EXP;
+ dec_val *= SMALL_NORMAL_VAL;
+ }
+ dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val);
}
// negate value if needed
diff --git a/tests/float/float_parse.py b/tests/float/float_parse.py
index 448eff3bc..de4ea455f 100644
--- a/tests/float/float_parse.py
+++ b/tests/float/float_parse.py
@@ -20,3 +20,8 @@ print(float('.' + '9' * 70 + 'e-50') == float('1e-50'))
print(float('.' + '0' * 60 + '1e10') == float('1e-51'))
print(float('.' + '0' * 60 + '9e25'))
print(float('.' + '0' * 60 + '9e40'))
+
+# ensure that accuracy is retained when value is close to a subnormal
+print(float('1.00000000000000000000e-37'))
+print(float('10.0000000000000000000e-38'))
+print(float('100.000000000000000000e-39'))
diff --git a/tests/float/float_parse_doubleprec.py b/tests/float/float_parse_doubleprec.py
index 356601130..2ea7842f3 100644
--- a/tests/float/float_parse_doubleprec.py
+++ b/tests/float/float_parse_doubleprec.py
@@ -14,3 +14,8 @@ print(float('.' + '9' * 400 + 'e-100'))
print(float('.' + '0' * 400 + '9e100'))
print(float('.' + '0' * 400 + '9e200'))
print(float('.' + '0' * 400 + '9e400'))
+
+# ensure that accuracy is retained when value is close to a subnormal
+print(float('1.00000000000000000000e-307'))
+print(float('10.0000000000000000000e-308'))
+print(float('100.000000000000000000e-309'))