8000 py/formatfloat: Calculate reference values with pow(). · dpwe/micropython@e172a5b · GitHub
[go: up one dir, main page]

Skip to content

Commit e172a5b

Browse files
committed
py/formatfloat: Calculate reference values with pow().
Formerly, ```formatfloat``` struggled with formatting exact powers of 10 as created by ```parsenum``` because of small differences in how those powers of 10 were calculated: ```formatfloat``` multiplied together a few values of 10^(2^Y) from a lookup table, but ```parsenum``` used ```pow(10, X)```. This was mitigated in micropython#8905 by adding 2eps of extra margin when comparing values ("aggressive rounding up"). This patch instead removes the discrepency by using ```pow()``` in ```formatfloat``` also. This eliminates the need for the 2eps margin, as well as the lookup tables. It is likely slower to run, however. In addition, this patch directly estimates the power-of-10 exponent from the power-of-2 exponent in the floating-point representation. This change surfaced a previously-noted problem with parsing numbers including trailing zeros. The fix to that problem, micropython#8980, is included in this PR to make the tests pass. Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
1 parent d147c19 commit e172a5b

File tree

1 file changed

+49
-42
lines changed

1 file changed

+49
-42
lines changed

py/parsenum.c

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m
166166
}
167167
}
168168

169+
169170
enum {
170171
REAL_IMAG_STATE_START = 0,
171172
REAL_IMAG_STATE_HAVE_REAL = 1,
@@ -178,31 +179,51 @@ typedef enum {
178179
PARSE_DEC_IN_EXP,
179180
} parse_dec_in_t;
180181

181-
#if MICROPY_PY_BUILTINS_COMPLEX
182-
mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex)
183-
#else
184-
mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex)
185-
#endif
186-
{
187-
#if MICROPY_PY_BUILTINS_FLOAT
188-
182+
#if MICROPY_PY_BUILTINS_FLOAT
189183
// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing
190184
// SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float
191185
// EXACT_POWER_OF_10 is the largest value of x so that 10^x can be stored exactly in a float
192186
// Note: EXACT_POWER_OF_10 is at least floor(log_5(2^mantissa_length)). Indeed, 10^n = 2^n * 5^n
193187
// so we only have to store the 5^n part in the mantissa (the 2^n part will go into the float's
194188
// exponent).
195-
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
189+
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
196190
#define DEC_VAL_MAX 1e20F
197191
#define SMALL_NORMAL_VAL (1e-37F)
198192
#define SMALL_NORMAL_EXP (-37)
199193
#define EXACT_POWER_OF_10 (9)
200-
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
194+
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
201195
#define DEC_VAL_MAX 1e200
202196
#define SMALL_NORMAL_VAL (1e-307)
203197
#define SMALL_NORMAL_EXP (-307)
204198
#define EXACT_POWER_OF_10 (22)
205-
#endif
199+
#endif
200+
201+
// Break out inner digit accumulation routine to ease trailing zero deferral.
202+
void accept_digit(mp_float_t *p_dec_val, int dig, int *p_exp_extra, int in) {
203+
// Core routine to ingest an additional digit.
204+
if (*p_dec_val < DEC_VAL_MAX) {
205+
// dec_val won't overflow so keep accumulating
206+
*p_dec_val = 10 * *p_dec_val + dig;
207+
if (in == PARSE_DEC_IN_FRAC) {
208+
--(*p_exp_extra);
209+
}
210+
} else {
211+
// dec_val might overflow and we anyway can't represent more digits
212+
// of precision, so ignore the digit and just adjust the exponent
213+
if (in == PARSE_DEC_IN_INTG) {
214+
++(*p_exp_extra);
215+
}
216+
}
217+
}
218+
#endif // MICROPY_BUILTINS_FLOAT
219+
220+
#if MICROPY_PY_BUILTINS_COMPLEX
221+
mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex)
222+
#else
223+
mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex)
224+
#endif
225+
{
226+
#if MICROPY_PY_BUILTINS_FLOAT
206227

207228
const char *top = str + len;
208229
mp_float_t dec_val = 0;
@@ -255,7 +276,7 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
255276
bool exp_neg = false;
256277
int exp_val = 0;
257278
int exp_extra = 0;
258-
int trailing_zeros = 0, trailing_zeros_frac = 0;
279+
int trailing_zeros_intg = 0, trailing_zeros_frac = 0;
259280
while (str < top) {
260281
unsigned int dig = *str++;
261282
if ('0' <= dig && dig <= '9') {
@@ -268,42 +289,28 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
268289
exp_val = 10 * exp_val + dig;
269290
}
270291
} else {
271-
if (dig == 0) {
292+
if (dig == 0 || dec_val >= DEC_VAL_MAX) {
272293
// Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them.
273-
++trailing_zeros;
274-
if (in == PARSE_DEC_IN_FRAC) {
294+
// Also, once we reach DEC_VAL_MAX, treat every additional digit as a trailing zero.
295+
if (in == PARSE_DEC_IN_INTG) {
296+
++trailing_zeros_intg;
297+
} else {
275298
++trailing_zeros_frac;
276299
}
277300
} else {
278-
// Time to un-defer any trailing zeros.
279-
if (trailing_zeros) {
280-
while (trailing_zeros) {
281-
if (dec_val < DEC_VAL_MAX) {
282-
dec_val = 10 * dec_val;
283-
if (trailing_zeros_frac) {
284-
--exp_extra;
285-
--trailing_zeros_frac;
286-
}
287-
67E6 }
288-
--trailing_zeros;
289-
}
290-
trailing_zeros_frac = 0;
301+
// Time to un-defer any trailing zeros. Intg zeros first.
302+
while (trailing_zeros_intg) {
303+
accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_INTG);
304+
--trailing_zeros_intg;
291305
}
292-
293-
if (dec_val < DEC_VAL_MAX) {
294-
// dec_val won't overflow so keep accumulating
295-
dec_val = 10 * dec_val + dig;
296-
if (in == PARSE_DEC_IN_FRAC) {
297-
--exp_extra;
298-
}
299-
} else {
300-
// dec_val might overflow and we anyway can't represent more digits
301-
// of precision, so ignore the digit and just adjust the exponent
302-
if (in == PARSE_DEC_IN_INTG) {
303-
++exp_extra;
304-
}
306+
while (trailing_zeros_frac) {
307+
accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_FRAC);
308+
--trailing_zeros_frac;
305309
}
310+
accept_digit(&dec_val, dig, &exp_extra, in);
306311
}
312+
//DEBUG_printf("dec_val %f exp_extra %d trail_z_i %d trail_z_f %d\n",
313+
// (double)dec_val, exp_extra, trailing_zeros_intg, trailing_zeros_frac);
307314
}
308315
} else if (in == PARSE_DEC_IN_INTG && dig == '.') {
309316
in = PARSE_DEC_IN_FRAC;
@@ -335,7 +342,7 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
335342
}
336343

337344
// apply the exponent, making sure it's not a subnormal value
338-
exp_val += (exp_extra + trailing_zeros - trailing_zeros_frac);
345+
exp_val += (exp_extra + trailing_zeros_intg);
339346
if (exp_val < SMALL_NORMAL_EXP) {
340347
exp_val -= SMALL_NORMAL_EXP;
341348
dec_val *= SMALL_NORMAL_VAL;

0 commit comments

Comments
 (0)
0