Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit af73bde

Browse files
committed
py/formatfloat: Special-case handling of whole parts uses integer math.
1 parent 0352e3e commit af73bde

File tree

2 files changed

+60
-56
lines changed

2 files changed

+60
-56
lines changed

py/formatfloat.c

Lines changed: 51 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,9 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
174174
int num_digits = 0;
175175
const FPTYPE *pos_pow = g_pos_pow;
176176
const FPTYPE *neg_pow = g_neg_pow;
177-
177+
uint32_t f_int = 0;
178+
uint32_t u_int = 0;
179+
178180
if (fp_iszero(f)) {
179181
e = 0;
180182
if (fmt == 'f') {
@@ -261,31 +263,40 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
261263
} else {
262264
// Build positive exponent
263265

264-
// First block is only for numbers that could be integers exactly-represented in float32.
265-
if (f < FPCONST(1e10)) {
266-
// In this case, we *won't* normalize f down to lie in 1 <= f < 10 because the
267-
// repeated scaling by 0.1 cumulates errors in the representation, meaning numbers
268-
// that start as integers become non-integers. Instead, we find the scale by
269-
// increasing a reference value through repeated multiplications by 10. This
270-
// whole number in the range 1e0..1e10 will be exactly represented.
271-
FPTYPE cumulated_power = FPCONST(1.0);
272-
for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++) {
273-
if ((cumulated_power * *pos_pow) <= f) {
274-
e += e1;
275-
cumulated_power *= *pos_pow;
276-
}
266+
// First block is only for numbers that could be integers
267+
// exactly-represented in float32. We handle the integer part
268+
// by casting into a uint32_t, so exclude numbers larger than that
269+
// limit (2^32, which is much larger than the 2^24 that is fully
270+
// represented in a float32).
271+
if (f < FPCONST(4294967296.0)) { // 2^32
272+
// In this case, we *won't* normalize f down to lie in 1 <= f < 10
273+
// because the repeated scaling by 0.1 cumulates errors in the
274+
// representation, meaning numbers that start as integers become
275+
// non-integers. Instead, we work in the integer domain and find
276+
// the scale by increasing a reference value through repeated
277+
// (integer) multiplications by 10.
278+
f_int = (uint32_t)f;
279+
u_int = 1;
280+
e = 0;
281+
while((u_int * 10L) <= f_int) {
282+
u_int *= 10L;
283+
++e;
277284
}
278-
// f is NOT normalized to be 1 <= f < 10; the mantissa printing is modified
279-
// to handle this.
285+
// Note: u_int == 10**e is re-used in the mantissa printing below.
286+
287+
// f is NOT normalized to be 1 <= f < 10; the mantissa printing is
288+
// modified to handle this.
280289
} else {
281-
// Calculate the exponent and normalize f to lie in the range 1 <= f < 10.
290+
// Calculate the exponent and normalize f to lie in the range
291+
// 1 <= f < 10.
282292
for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
283293
if (*pos_pow <= f) {
284294
e += e1;
285295
f *= *neg_pow;
286296
}
287297
}
288-
// It can be that f was right on the edge of an entry in pos_pow needs to be reduced
298+
// It can be that f was right on the edge of an entry in pos_pow
299+
// needs to be reduced
289300
if ((int)f >= 10) {
290301
e += 1;
291302
f *= FPCONST(0.1);
@@ -341,9 +352,9 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
341352
// before the decimal.
342353
//
343354
// For numbers in the range 1e1 to 1e10 (which includes all whole-values
344-
// that can be exactly represented in float32s), we do NOT normalize f
345-
// to avoid the small departures from whole numbers the scaling would
346-
// incur.
355+
// that can be exactly, continuously represented in float32s), we do NOT
356+
// normalize f, to avoid the small departures from whole numbers the
357+
// scaling would incur.
347358

348359
// For e, prec is # digits after the decimal
349360
// For f, prec is # digits after the decimal
@@ -362,50 +373,36 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
362373
}
363374

364375
int num_digits_left = num_digits;
365-
if (f >= FPCONST(10.0)) {
376+
// If f_int is nonzero, it's because 1.0 < f < 2**32, so the integer
377+
// part is fully represented in f_int (a uint32_t).
378+
if (f_int >= 10) {
366379
// Print any leading digits to bring f down to < 10.
367-
// As above, we avoid modifying f to avoid distorting whole numbers.
368-
// Instead, we construct a comparison value that is always a whole
369-
// number (because it does not involve multiplying by any non-whole
370-
// numbers) and subtract that as we print each digit.
380+
// We work with an integer version of the whole-number part of f,
381+
// then adjust f only by subtracting integers as we print each
382+
// digit. This avoids distorting f via multiplication by inexact
383+
// negative powers of 10.
384+
// u_int was already set to 10**e when f_int was set up.
371385
for (int digit_index = e; digit_index > 0; --digit_index) {
372-
// Construct the power-of-10 "unit" for this digit by multiplying
373-
// up powers of 10 (so it remains a whole number as long as
374-
// possible). Because we start with the highest-value digit,
375-
// and because dividing by 10 would spoil our guarantee of
376-
// wholeness, we build it up from scratch every digit.
377-
FPTYPE unit = FPCONST(1.0);
378-
// Re-use the idiom from finding e to make unit = 10^digit_index.
379-
const FPTYPE *pos_pow_2 = g_pos_pow;
380-
for (int ee = digit_index, e2 = FPDECEXP; e2; e2 >>= 1, pos_pow_2++) {
381-
if (ee >= e2) {
382-
ee -= e2;
383-
unit *= *pos_pow_2;
384-
}
385-
}
386-
// Step through the 10 possible values of this digit until we
387-
// find the one before going beyond f.
388-
int d;
389-
FPTYPE cumulated_units = FPCONST(0.0);
390-
for (d = 0; d < 10; ++d) {
391-
if (f < cumulated_units + unit) {
392-
break;
393-
}
394-
cumulated_units += unit;
395-
}
386+
// Integer division will safely give us the leading digit.
387+
int d = f_int / u_int;
388+
f_int -= (d * u_int);
389+
// Reduce our full floating point value by the integer value we're
390+
// printing.
391+
f -= (FPTYPE)(d * u_int);
396392
*s++ = '0' + d;
397-
f -= cumulated_units;
398393
if (dec == 0 && prec > 0) {
399394
*s++ = '.';
400395
}
401396
--dec;
402397
--num_digits_left;
398+
// Integer division safely reduces the integer unit base.
399+
u_int /= 10;
403400
// Special case the last digit.
404401
if (num_digits_left == 0) {
405-
// We're going to fall through to the rounding code which expects
402+
// We're going to fall through to rounding code which expects
406403
// f to be a residual in the range 1 <= f < 10, and will apply
407-
// rounding if it's >= 5. So now we need to construct that residual.
408-
f *= (FPCONST(10.) / unit);
404+
// rounding if it's >= 5. So we need to construct the residual.
405+
f *= (FPCONST(1.) / u_int);
409406
break;
410407
}
411408
}
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
# Test that integers format to exact values.
22

33
for b in [13, 123, 457, 23456]:
4-
for r in range(1, 3):
4+
for r in range(1, 6):
55
e_fmt = "{:." + str(r) + "e}"
66
f_fmt = "{:." + str(r) + "f}"
77
g_fmt = "{:." + str(r) + "g}"
8-
for e in range(-9, 5):
8+
for e in range(0, 5):
99
f = b * (10**e)
1010
title = str(b) + "x 10^" + str(e)
1111
print(title, "with format", e_fmt, "gives", e_fmt.format(f))
1212
print(title, "with format", f_fmt, "gives", f_fmt.format(f))
1313
print(title, "with format", g_fmt, "gives", g_fmt.format(f))
14+
15+
# The largest integer you can store exactly in float32.
16+
print("{:f}".format(16777215))
17+
# The largest integer smaller than 4e9 which can be exactly represented in a
18+
# float32. Space between adjacent values is 256 at this point.
19+
# 4e9 is the threshold for the special-case code in formatfloat.c.
20+
print("{:f}".format(3999999744))

0 commit comments

Comments
 (0)