diff --git a/py/parsenum.c b/py/parsenum.c
index 98e7736851d123926ce2f55712b3c638a86bac0a..124489c66ef1c81e35441a7d696c6bd32816a633 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 448eff3bc909f4aa1064a1fc6b6c1976e753d03b..de4ea455fb85103eefe91d3473fd01bdb57234a7 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 356601130998e2c0c034b71b6c8ddbdaccbfabb7..2ea7842f3f6271e87fa935e9f08e8dadb35820e7 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'))