Skip to content

Commit 5fdd249

Browse files
yoctopucedpgeorge
authored andcommitted
py/parsenum: Reduce code footprint of mp_parse_num_float.
The mantissa parsing code uses a floating point variable to accumulate digits. Using an `mp_float_uint_t` variable instead and casting to `mp_float_t` at the very end reduces code size. In some cases, it also improves the rounding behaviour as extra digits are taken into account by the int-to-float conversion code. An extra test case handles the special case where mantissa overflow occurs while processing deferred trailing zeros. Signed-off-by: Yoctopuce dev <[email protected]>
1 parent 50fab08 commit 5fdd249

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed

py/parsenum.c

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -179,39 +179,40 @@ typedef enum {
179179
} parse_dec_in_t;
180180

181181
#if MICROPY_PY_BUILTINS_FLOAT
182-
// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing
182+
// MANTISSA_MAX is used to retain precision while not overflowing mantissa
183183
// SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float
184184
// EXACT_POWER_OF_10 is the largest value of x so that 10^x can be stored exactly in a float
185185
// Note: EXACT_POWER_OF_10 is at least floor(log_5(2^mantissa_length)). Indeed, 10^n = 2^n * 5^n
186186
// so we only have to store the 5^n part in the mantissa (the 2^n part will go into the float's
187187
// exponent).
188188
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
189-
#define DEC_VAL_MAX 1e20F
189+
#define MANTISSA_MAX 0x19999998U
190190
#define SMALL_NORMAL_VAL (1e-37F)
191191
#define SMALL_NORMAL_EXP (-37)
192192
#define EXACT_POWER_OF_10 (9)
193193
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
194-
#define DEC_VAL_MAX 1e200
194+
#define MANTISSA_MAX 0x1999999999999998ULL
195195
#define SMALL_NORMAL_VAL (1e-307)
196196
#define SMALL_NORMAL_EXP (-307)
197197
#define EXACT_POWER_OF_10 (22)
198198
#endif
199199

200200
// Break out inner digit accumulation routine to ease trailing zero deferral.
201-
static void accept_digit(mp_float_t *p_dec_val, int dig, int *p_exp_extra, int in) {
201+
static mp_float_uint_t accept_digit(mp_float_uint_t p_mantissa, unsigned int dig, int *p_exp_extra, int in) {
202202
// Core routine to ingest an additional digit.
203-
if (*p_dec_val < DEC_VAL_MAX) {
203+
if (p_mantissa < MANTISSA_MAX) {
204204
// dec_val won't overflow so keep accumulating
205-
*p_dec_val = 10 * *p_dec_val + dig;
206205
if (in == PARSE_DEC_IN_FRAC) {
207206
--(*p_exp_extra);
208207
}
208+
return 10u * p_mantissa + dig;
209209
} else {
210210
// dec_val might overflow and we anyway can't represent more digits
211211
// of precision, so ignore the digit and just adjust the exponent
212212
if (in == PARSE_DEC_IN_INTG) {
213213
++(*p_exp_extra);
214214
}
215+
return p_mantissa;
215216
}
216217
}
217218
#endif // MICROPY_PY_BUILTINS_FLOAT
@@ -273,6 +274,7 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
273274
// string should be a decimal number
274275
parse_dec_in_t in = PARSE_DEC_IN_INTG;
275276
bool exp_neg = false;
277+
mp_float_uint_t mantissa = 0;
276278
int exp_val = 0;
277279
int exp_extra = 0;
278280
int trailing_zeros_intg = 0, trailing_zeros_frac = 0;
@@ -288,9 +290,9 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
288290
exp_val = 10 * exp_val + dig;
289291
}
290292
} else {
291-
if (dig == 0 || dec_val >= DEC_VAL_MAX) {
293+
if (dig == 0 || mantissa >= MANTISSA_MAX) {
292294
// Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them.
293-
// Also, once we reach DEC_VAL_MAX, treat every additional digit as a trailing zero.
295+
// Also, once we reach MANTISSA_MAX, treat every additional digit as a trailing zero.
294296
if (in == PARSE_DEC_IN_INTG) {
295297
++trailing_zeros_intg;
296298
} else {
@@ -299,14 +301,14 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
299301
} else {
300302
// Time to un-defer any trailing zeros. Intg zeros first.
301303
while (trailing_zeros_intg) {
302-
accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_INTG);
304+
mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_INTG);
303305
--trailing_zeros_intg;
304306
}
305307
while (trailing_zeros_frac) {
306-
accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_FRAC);
308+
mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_FRAC);
307309
--trailing_zeros_frac;
308310
}
309-
accept_digit(&dec_val, dig, &exp_extra, in);
311+
mantissa = accept_digit(mantissa, dig, &exp_extra, in);
310312
}
311313
}
312314
} else if (in == PARSE_DEC_IN_INTG && dig == '.') {
@@ -340,6 +342,7 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
340342

341343
// apply the exponent, making sure it's not a subnormal value
342344
exp_val += exp_extra + trailing_zeros_intg;
345+
dec_val = (mp_float_t)mantissa;
343346
if (exp_val < SMALL_NORMAL_EXP) {
344347
exp_val -= SMALL_NORMAL_EXP;
345348
dec_val *= SMALL_NORMAL_VAL;

tests/float/float_parse.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
print(float("1e18446744073709551621"))
3232
print(float("1e-18446744073709551621"))
3333

34+
# mantissa overflow while processing deferred trailing zeros
35+
print(float("10000000000000000000001"))
36+
3437
# check small decimals are as close to their true value as possible
3538
for n in range(1, 10):
3639
print(float("0.%u" % n) == n / 10)

0 commit comments

Comments
 (0)