Skip to content

Commit a621198

Browse files
[libc] Move printf long double to simple calc (#75414)
The Ryu algorithm is very fast with its table, but that table grows too large for long doubles. This patch adds a method of calculating the digits of long doubles using just wide integers and fast modulo operations. This results in significant performance improvements vs the previous int calc mode, while taking up a similar amound of peak memory. It will be slow in some %e/%g cases, but reasonable fast for %f with no loss of accuracy.
1 parent b051141 commit a621198

File tree

6 files changed

+1106
-1016
lines changed

6 files changed

+1106
-1016
lines changed

libc/config/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"doc": "Disable handling of %n in printf format string."
1414
},
1515
"LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE": {
16-
"value": true,
16+
"value": false,
1717
"doc": "Use large table for better printf long double performance."
1818
}
1919
},

libc/docs/dev/printf_behavior.rst

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ C standard and POSIX standard. If any behavior is not mentioned here, it should
1313
be assumed to follow the behavior described in those standards.
1414

1515
The LLVM-libc codebase is under active development, and may change. This
16-
document was last updated [August 18, 2023] by [michaelrj] and may
16+
document was last updated [January 8, 2024] by [michaelrj] and may
1717
not be accurate after this point.
1818

1919
The behavior of LLVM-libc's printf is heavily influenced by compile-time flags.
@@ -87,14 +87,26 @@ are not recommended to be adjusted except by persons familiar with the Printf
8787
Ryu Algorithm. Additionally they have no effect when float conversions are
8888
disabled.
8989

90+
LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD
91+
---------------------------------------
92+
This flag disables the separate long double conversion implementation. It is
93+
not based on the Ryu algorithm, instead generating the digits by
94+
multiplying/dividing the written-out number by 10^9 to get blocks. It's
95+
significantly faster than INT_CALC, only about 10x slower than MEGA_TABLE,
96+
and is small in binary size. Its downside is that it always calculates all
97+
of the digits above the decimal point, making it slightly inefficient for %e
98+
calls with large exponents. This is the default. This specialization overrides
99+
other flags, so this flag must be set for other flags to effect the long double
100+
behavior.
101+
90102
LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
91103
-------------------------------------------------
92104
When set, the float to string decimal conversion algorithm will use a larger
93105
table to accelerate long double conversions. This larger table is around 5MB of
94-
size when compiled. This flag is enabled by default in the CMake.
106+
size when compiled.
95107

96-
LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT(_LD)
97-
--------------------------------------------
108+
LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT
109+
---------------------------------------
98110
When set, the float to string decimal conversion algorithm will use dyadic
99111
floats instead of a table when performing floating point conversions. This
100112
results in ~50 digits of accuracy in the result, then zeroes for the remaining
@@ -107,8 +119,7 @@ LIBC_COPT_FLOAT_TO_STR_USE_INT_CALC
107119
When set, the float to string decimal conversion algorithm will use wide
108120
integers instead of a table when performing floating point conversions. This
109121
gives the same results as the table, but is very slow at the extreme ends of
110-
the long double range. If no flags are set this is the default behavior for
111-
long double conversions.
122+
the long double range.
112123

113124
LIBC_COPT_FLOAT_TO_STR_NO_TABLE
114125
-------------------------------

libc/src/__support/UInt.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,17 @@ namespace LIBC_NAMESPACE::cpp {
2727

2828
template <size_t Bits, bool Signed> struct BigInt {
2929

30+
// This being hardcoded as 64 is okay because we're using uint64_t as our
31+
// internal type which will always be 64 bits.
32+
using word_type = uint64_t;
33+
LIBC_INLINE_VAR static constexpr size_t WORD_SIZE =
34+
sizeof(word_type) * CHAR_BIT;
35+
36+
// TODO: Replace references to 64 with WORD_SIZE, and uint64_t with word_type.
3037
static_assert(Bits > 0 && Bits % 64 == 0,
3138
"Number of bits in BigInt should be a multiple of 64.");
3239
LIBC_INLINE_VAR static constexpr size_t WORDCOUNT = Bits / 64;
33-
cpp::array<uint64_t, WORDCOUNT> val{};
40+
cpp::array<word_type, WORDCOUNT> val{};
3441

3542
LIBC_INLINE_VAR static constexpr uint64_t MASK32 = 0xFFFFFFFFu;
3643

@@ -448,6 +455,8 @@ template <size_t Bits, bool Signed> struct BigInt {
448455
// pos is the index of the current 64-bit chunk that we are processing.
449456
size_t pos = WORDCOUNT;
450457

458+
// TODO: look into if constexpr(Bits > 256) skip leading zeroes.
459+
451460
for (size_t q_pos = WORDCOUNT - lower_pos; q_pos > 0; --q_pos) {
452461
// q_pos is 1 + the index of the current 64-bit chunk of the quotient
453462
// being processed.

0 commit comments

Comments
 (0)