Skip to content

Commit 97c2a2a

Browse files
committed
Address review nits from 2025-01-27
1 parent d38304e commit 97c2a2a

File tree

1 file changed

+41
-20
lines changed

1 file changed

+41
-20
lines changed

libc/src/stdio/printf_core/float_dec_converter_limited.h

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "src/__support/FPUtil/dyadic_float.h"
4949
#include "src/__support/FPUtil/rounding_mode.h"
5050
#include "src/__support/integer_to_string.h"
51+
#include "src/__support/libc_assert.h"
5152
#include "src/__support/macros/config.h"
5253
#include "src/stdio/printf_core/core_structs.h"
5354
#include "src/stdio/printf_core/float_inf_nan_converter.h"
@@ -107,6 +108,14 @@ struct DigitsOutput {
107108
char digits[MAX_DIGITS + 1];
108109
};
109110

111+
// Estimate log10 of a power of 2, by multiplying its exponent by
112+
// 1292913986/2^32. That is a rounded-down approximation to log10(2), accurate
113+
// enough that for any binary exponent in the range of float128 it will give
114+
// the correct value of floor(log10(2^n)).
115+
int estimate_log10(int exponent_of_2) {
116+
return (exponent_of_2 * 1292913986LL) >> 32;
117+
}
118+
110119
// Calculate the actual digits of a decimal representation of an FP number.
111120
//
112121
// If `e_mode` is true, then `precision` indicates the desired number of output
@@ -139,22 +148,20 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
139148
return output;
140149
}
141150

142-
// Estimate log10 of the input value, by multiplying its binary exponent by
143-
// 1292913986/2^32. That is a rounded-down approximation to log10(2),
144-
// accurate enough that for any binary exponent in the range of float128 it
145-
// will give the correct value of floor(log10(2^n)).
151+
// Calculate bounds on log10 of the input value. Its binary exponent bounds
152+
// the value between two powers of 2, and we use estimate_log10 to determine
153+
// log10 of each of those.
146154
//
147-
// This estimate is correct for deciding how many decimal digits we end up
148-
// producing, unless a power of 10 falls in the interval corresponding to
149-
// this binary exponent, in which case there might be one more decimal digit
150-
// for larger mantissas. To detect this, we do the same computation for the
151-
// next exponent up.
152-
int log10_input_min = ((input.exponent - 1) * 1292913986LL) >> 32;
153-
int log10_input_max = (input.exponent * 1292913986LL) >> 32;
155+
// If a power of 10 falls in the interval between those powers of 2, then
156+
// log10_input_min and log10_input_max will differ by 1, and the correct
157+
// decimal exponent of the output will be one of those two values. If no
158+
// power of 10 is in the interval, then these two values will be equal and
159+
// there is only one choice for the decimal exponent.
160+
int log10_input_min = estimate_log10(input.exponent - 1);
161+
int log10_input_max = estimate_log10(input.exponent);
154162

155163
// Make a DyadicFloat containing the value 10, to use as the base for
156-
// exponentation inside the following loop, potentially more than once if we
157-
// need to iterate.
164+
// exponentiation.
158165
fputil::DyadicFloat<DF_BITS> ten(Sign::POS, 1, 5);
159166

160167
// Compute the exponent of the lowest-order digit we want as output. In F
@@ -213,6 +220,9 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
213220
// Convert the mantissa into a DyadicFloat, making sure it has the right
214221
// sign, so that directed rounding will go in the right direction, if
215222
// enabled.
223+
//
224+
// (The fixed -127 adjustment on the exponent is because input.mantissa is a
225+
// UInt128, and the leading 1 bit has been aligned to the top of it.)
216226
fputil::DyadicFloat<DF_BITS> flt_mantissa(input.sign, input.exponent - 127,
217227
input.mantissa);
218228

@@ -260,7 +270,7 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
260270
// X.YZe+NN and instead got WX.YZe+NN. So when we shorten the digit string
261271
// by one, we'll also need to increment the output exponent.
262272
if (output.ndigits > size_t(precision)) {
263-
assert(output.ndigits == size_t(precision) + 1);
273+
LIBC_ASSERT(output.ndigits == size_t(precision) + 1);
264274
need_reround = true;
265275
output.exponent++;
266276
}
@@ -280,7 +290,7 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
280290
// one digit too long, or else we'd have spotted the problem in advance and
281291
// flipped into E mode already.
282292
if (output.ndigits > MAX_DIGITS) {
283-
assert(output.ndigits == MAX_DIGITS + 1);
293+
LIBC_ASSERT(output.ndigits == MAX_DIGITS + 1);
284294
need_reround = true;
285295
}
286296
}
@@ -318,9 +328,12 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
318328
// Extract the two relevant digits. round_digit is the one we're removing;
319329
// new_low_digit is the last one we're keeping, so we need to know if it's
320330
// even or odd to handle exact tie cases (when round_dir == 0).
321-
int round_digit = output.digits[--output.ndigits] - '0';
331+
--output.ndigits;
332+
int round_digit = internal::b36_char_to_int(output.digits[output.ndigits]);
322333
int new_low_digit =
323-
output.ndigits == 0 ? 0 : output.digits[output.ndigits - 1] - '0';
334+
output.ndigits == 0
335+
? 0
336+
: internal::b36_char_to_int(output.digits[output.ndigits - 1]);
324337

325338
// Make a binary number that we can pass to `fputil::rounding_direction`.
326339
// We put new_low_digit at bit 8, and imagine that we're rounding away the
@@ -330,6 +343,11 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
330343
//
331344
// Then we adjust by +1 or -1 based on round_dir if the round digit is 5,
332345
// as described above.
346+
//
347+
// The subexpression `(round_digit * 0x19a) >> 4` is computing the
348+
// expression (256/10 * round_digit) mentioned above, accurately enough to
349+
// map 5 to exactly 128 but avoiding an integer division (for platforms
350+
// where it's slow, e.g. not in hardware).
333351
LIBC_NAMESPACE::UInt<64> round_word = (new_low_digit * 256) +
334352
((round_digit * 0x19a) >> 4) +
335353
(round_digit == 5 ? -round_dir : 0);
@@ -343,9 +361,12 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
343361
// at a time. (A bit painful, but better than going back to the integer
344362
// we made it from and doing the decimal conversion all over again.)
345363
for (size_t i = output.ndigits; i-- > 0;) {
346-
if (output.digits[i]++ != '9')
364+
if (output.digits[i] != '9') {
365+
output.digits[i]++;
347366
break;
348-
output.digits[i] = '0';
367+
} else {
368+
output.digits[i] = '0';
369+
}
349370
}
350371
}
351372
}
@@ -527,7 +548,7 @@ LIBC_INLINE int convert_float_inner(Writer *writer,
527548
radix::Dec::WithWidth<2>::WithSign>
528549
expcvt{output.exponent};
529550
cpp::string_view expview = expcvt.view();
530-
expbuf[0] = (to_conv.conv_name & 32) | 'E';
551+
expbuf[0] = internal::islower(to_conv.conv_name) ? 'e' : 'E';
531552
explen = expview.size() + 1;
532553
__builtin_memcpy(expbuf + 1, expview.data(), expview.size());
533554
}

0 commit comments

Comments
 (0)