mirror of
https://github.com/fmtlib/fmt.git
synced 2025-11-27 12:49:46 +01:00
make FP formatting available to be used at compile-time (#2426)
* works only with FMT_HEADER_ONLY * works only with float and double types (not long double)
This commit is contained in:
@@ -152,6 +152,75 @@ template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
|
||||
// log10(2) = 0x0.4d104d427de7fbcc...
|
||||
static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc;
|
||||
|
||||
template <typename T = void> struct basic_impl_data {
|
||||
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||
// These are generated by support/compute-powers.py.
|
||||
static constexpr uint64_t pow10_significands[87] = {
|
||||
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
|
||||
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
|
||||
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
|
||||
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
|
||||
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
|
||||
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
|
||||
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
|
||||
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
|
||||
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
|
||||
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
|
||||
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
|
||||
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
|
||||
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
|
||||
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
|
||||
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
|
||||
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
|
||||
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
|
||||
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
|
||||
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||
};
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
FMT_GCC_PRAGMA("GCC diagnostic push")
|
||||
FMT_GCC_PRAGMA("GCC diagnostic ignored \"-Wnarrowing\"")
|
||||
#endif
|
||||
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||
// to significands above.
|
||||
static constexpr int16_t pow10_exponents[87] = {
|
||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
|
||||
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
|
||||
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
FMT_GCC_PRAGMA("GCC diagnostic pop")
|
||||
#endif
|
||||
|
||||
static constexpr uint64_t power_of_10_64[20] = {
|
||||
1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
|
||||
10000000000000000000ULL};
|
||||
};
|
||||
|
||||
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
|
||||
struct impl_data : basic_impl_data<> {};
|
||||
|
||||
#if __cplusplus < 201703L
|
||||
template <typename T>
|
||||
constexpr uint64_t basic_impl_data<T>::pow10_significands[];
|
||||
template <typename T> constexpr int16_t basic_impl_data<T>::pow10_exponents[];
|
||||
template <typename T> constexpr uint64_t basic_impl_data<T>::power_of_10_64[];
|
||||
#endif
|
||||
|
||||
template <typename T> struct bits {
|
||||
static FMT_CONSTEXPR_DECL const int value =
|
||||
static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
|
||||
@@ -275,53 +344,8 @@ FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
|
||||
|
||||
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
|
||||
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
|
||||
inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
||||
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||
// These are generated by support/compute-powers.py.
|
||||
static constexpr const uint64_t pow10_significands[] = {
|
||||
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
|
||||
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
|
||||
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
|
||||
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
|
||||
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
|
||||
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
|
||||
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
|
||||
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
|
||||
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
|
||||
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
|
||||
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
|
||||
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
|
||||
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
|
||||
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
|
||||
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
|
||||
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
|
||||
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
|
||||
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
|
||||
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||
};
|
||||
|
||||
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||
// to significands above.
|
||||
static constexpr const int16_t pow10_exponents[] = {
|
||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
|
||||
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
|
||||
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
|
||||
|
||||
FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
|
||||
int& pow10_exponent) {
|
||||
const int shift = 32;
|
||||
const auto significand = static_cast<int64_t>(log10_2_significand);
|
||||
int index = static_cast<int>(
|
||||
@@ -335,7 +359,8 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
|
||||
const int dec_exp_step = 8;
|
||||
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||
return {pow10_significands[index], pow10_exponents[index]};
|
||||
return {impl_data::pow10_significands[index],
|
||||
impl_data::pow10_exponents[index]};
|
||||
}
|
||||
|
||||
// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
|
||||
@@ -636,13 +661,6 @@ enum result {
|
||||
};
|
||||
}
|
||||
|
||||
inline uint64_t power_of_10_64(int exp) {
|
||||
static constexpr const uint64_t data[] = {1, FMT_POWERS_OF_10(1),
|
||||
FMT_POWERS_OF_10(1000000000ULL),
|
||||
10000000000000000000ULL};
|
||||
return data[exp];
|
||||
}
|
||||
|
||||
// Generates output using the Grisu digit-gen algorithm.
|
||||
// error: the size of the region (lower, upper) outside of which numbers
|
||||
// definitely do not round to value (Delta in Grisu3).
|
||||
@@ -662,7 +680,7 @@ FMT_INLINE FMT_CONSTEXPR digits::result grisu_gen_digits(fp value,
|
||||
uint64_t fractional = value.f & (one.f - 1);
|
||||
exp = count_digits(integral); // kappa in Grisu.
|
||||
// Divide by 10 to prevent overflow.
|
||||
auto result = handler.on_start(power_of_10_64(exp - 1) << -one.e,
|
||||
auto result = handler.on_start(impl_data::power_of_10_64[exp - 1] << -one.e,
|
||||
value.f / 10, error * 10, exp);
|
||||
if (result != digits::more) return result;
|
||||
// Generate digits for the integral part. This can produce up to 10 digits.
|
||||
@@ -712,8 +730,8 @@ FMT_INLINE FMT_CONSTEXPR digits::result grisu_gen_digits(fp value,
|
||||
--exp;
|
||||
auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
|
||||
result = handler.on_digit(static_cast<char>('0' + digit),
|
||||
power_of_10_64(exp) << -one.e, remainder, error,
|
||||
exp, true);
|
||||
impl_data::power_of_10_64[exp] << -one.e,
|
||||
remainder, error, exp, true);
|
||||
if (result != digits::more) return result;
|
||||
} while (exp > 0);
|
||||
// Generate digits for the fractional part.
|
||||
@@ -2225,8 +2243,8 @@ small_divisor_case_label:
|
||||
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
||||
// https://fmt.dev/papers/p372-steele.pdf.
|
||||
template <typename Double>
|
||||
void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
|
||||
int& exp10) {
|
||||
FMT_CONSTEXPR20 void fallback_format(Double d, int num_digits, bool binary32,
|
||||
buffer<char>& buf, int& exp10) {
|
||||
bigint numerator; // 2 * R in (FPP)^2.
|
||||
bigint denominator; // 2 * S in (FPP)^2.
|
||||
// lower and upper are differences between value and corresponding boundaries.
|
||||
@@ -2342,7 +2360,9 @@ void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
||||
FMT_HEADER_ONLY_CONSTEXPR20 int format_float(T value, int precision,
|
||||
float_specs specs,
|
||||
buffer<char>& buf) {
|
||||
static_assert(!std::is_same<T, float>::value, "");
|
||||
FMT_ASSERT(value >= 0, "value is negative");
|
||||
|
||||
@@ -2353,13 +2373,13 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
||||
return 0;
|
||||
}
|
||||
buf.try_resize(to_unsigned(precision));
|
||||
std::uninitialized_fill_n(buf.data(), precision, '0');
|
||||
fill_n(buf.data(), precision, '0');
|
||||
return -precision;
|
||||
}
|
||||
|
||||
if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
|
||||
|
||||
if (precision < 0) {
|
||||
if (!is_constant_evaluated() && precision < 0) {
|
||||
// Use Dragonbox for the shortest format.
|
||||
if (specs.binary32) {
|
||||
auto dec = dragonbox::to_decimal(static_cast<float>(value));
|
||||
@@ -2385,7 +2405,8 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
||||
const int max_double_digits = 767;
|
||||
if (precision > max_double_digits) precision = max_double_digits;
|
||||
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
||||
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) {
|
||||
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error ||
|
||||
is_constant_evaluated()) {
|
||||
exp += handler.size - cached_exp10 - 1;
|
||||
fallback_format(value, handler.precision, specs.binary32, buf, exp);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user