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:
parent
d9fd695ac7
commit
b4d9d82e1d
@ -864,10 +864,6 @@ template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
inline bool isfinite(T) {
|
||||
return true;
|
||||
}
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
inline bool isfinite(T value) {
|
||||
return std::isfinite(value);
|
||||
}
|
||||
|
||||
// Converts value to int and checks that it's in the range [0, upper).
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
|
@ -1178,8 +1178,8 @@ template <typename Context> class value {
|
||||
constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
|
||||
FMT_INLINE value(int128_t val) : int128_value(val) {}
|
||||
FMT_INLINE value(uint128_t val) : uint128_value(val) {}
|
||||
FMT_INLINE value(float val) : float_value(val) {}
|
||||
FMT_INLINE value(double val) : double_value(val) {}
|
||||
constexpr FMT_INLINE value(float val) : float_value(val) {}
|
||||
constexpr FMT_INLINE value(double val) : double_value(val) {}
|
||||
FMT_INLINE value(long double val) : long_double_value(val) {}
|
||||
constexpr FMT_INLINE value(bool val) : bool_value(val) {}
|
||||
constexpr FMT_INLINE value(char_type val) : char_value(val) {}
|
||||
|
@ -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 {
|
||||
|
@ -261,6 +261,12 @@ inline auto ctzll(uint64_t x) -> int {
|
||||
FMT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
#ifdef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY_CONSTEXPR20 FMT_CONSTEXPR20
|
||||
#else
|
||||
# define FMT_HEADER_ONLY_CONSTEXPR20
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
|
||||
@ -1247,7 +1253,7 @@ constexpr auto exponent_mask() ->
|
||||
|
||||
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||
template <typename Char, typename It>
|
||||
auto write_exponent(int exp, It it) -> It {
|
||||
FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It {
|
||||
FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
|
||||
if (exp < 0) {
|
||||
*it++ = static_cast<Char>('-');
|
||||
@ -1268,16 +1274,19 @@ auto write_exponent(int exp, It it) -> It {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto format_float(T value, int precision, float_specs specs, buffer<char>& buf)
|
||||
-> int;
|
||||
FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision,
|
||||
float_specs specs,
|
||||
buffer<char>& buf) -> int;
|
||||
|
||||
// Formats a floating-point number with snprintf.
|
||||
template <typename T>
|
||||
auto snprintf_float(T value, int precision, float_specs specs,
|
||||
buffer<char>& buf) -> int;
|
||||
|
||||
template <typename T> auto promote_float(T value) -> T { return value; }
|
||||
inline auto promote_float(float value) -> double {
|
||||
template <typename T> constexpr auto promote_float(T value) -> T {
|
||||
return value;
|
||||
}
|
||||
constexpr auto promote_float(float value) -> double {
|
||||
return static_cast<double>(value);
|
||||
}
|
||||
|
||||
@ -1649,7 +1658,8 @@ FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs<Char> specs,
|
||||
FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isinf,
|
||||
basic_format_specs<Char> specs,
|
||||
const float_specs& fspecs) -> OutputIt {
|
||||
auto str =
|
||||
isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan");
|
||||
@ -1673,7 +1683,7 @@ struct big_decimal_fp {
|
||||
int exponent;
|
||||
};
|
||||
|
||||
inline auto get_significand_size(const big_decimal_fp& fp) -> int {
|
||||
constexpr auto get_significand_size(const big_decimal_fp& fp) -> int {
|
||||
return fp.significand_size;
|
||||
}
|
||||
template <typename T>
|
||||
@ -1682,7 +1692,7 @@ inline auto get_significand_size(const dragonbox::decimal_fp<T>& fp) -> int {
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
inline auto write_significand(OutputIt out, const char* significand,
|
||||
constexpr auto write_significand(OutputIt out, const char* significand,
|
||||
int significand_size) -> OutputIt {
|
||||
return copy_str<Char>(significand, significand + significand_size, out);
|
||||
}
|
||||
@ -1691,11 +1701,10 @@ inline auto write_significand(OutputIt out, UInt significand,
|
||||
int significand_size) -> OutputIt {
|
||||
return format_decimal<Char>(out, significand, significand_size).end;
|
||||
}
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
inline auto write_significand(OutputIt out, T significand, int significand_size,
|
||||
int exponent,
|
||||
const digit_grouping<Char>& grouping)
|
||||
-> OutputIt {
|
||||
template <typename Char, typename OutputIt, typename T, typename Grouping>
|
||||
FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
|
||||
int significand_size, int exponent,
|
||||
const Grouping& grouping) -> OutputIt {
|
||||
if (!grouping.separator()) {
|
||||
out = write_significand<Char>(out, significand, significand_size);
|
||||
return detail::fill_n(out, exponent, static_cast<Char>('0'));
|
||||
@ -1736,7 +1745,7 @@ inline auto write_significand(OutputIt out, UInt significand,
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
inline auto write_significand(OutputIt out, const char* significand,
|
||||
FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand,
|
||||
int significand_size, int integral_size,
|
||||
Char decimal_point) -> OutputIt {
|
||||
out = detail::copy_str_noinline<Char>(significand,
|
||||
@ -1747,11 +1756,11 @@ inline auto write_significand(OutputIt out, const char* significand,
|
||||
significand + significand_size, out);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char, typename T>
|
||||
inline auto write_significand(OutputIt out, T significand, int significand_size,
|
||||
int integral_size, Char decimal_point,
|
||||
const digit_grouping<Char>& grouping)
|
||||
-> OutputIt {
|
||||
template <typename OutputIt, typename Char, typename T, typename Grouping>
|
||||
FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
|
||||
int significand_size, int integral_size,
|
||||
Char decimal_point,
|
||||
const Grouping& grouping) -> OutputIt {
|
||||
if (!grouping.separator()) {
|
||||
return write_significand(out, significand, significand_size, integral_size,
|
||||
decimal_point);
|
||||
@ -1765,13 +1774,15 @@ inline auto write_significand(OutputIt out, T significand, int significand_size,
|
||||
buffer.end(), out);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename DecimalFP, typename Char>
|
||||
auto write_float(OutputIt out, const DecimalFP& fp,
|
||||
const basic_format_specs<Char>& specs, float_specs fspecs,
|
||||
locale_ref loc) -> OutputIt {
|
||||
template <typename OutputIt, typename DecimalFP, typename Char,
|
||||
typename Grouping = digit_grouping<Char>>
|
||||
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp,
|
||||
const basic_format_specs<Char>& specs,
|
||||
float_specs fspecs, locale_ref loc)
|
||||
-> OutputIt {
|
||||
auto significand = fp.significand;
|
||||
int significand_size = get_significand_size(fp);
|
||||
static const Char zero = static_cast<Char>('0');
|
||||
constexpr Char zero = static_cast<Char>('0');
|
||||
auto sign = fspecs.sign;
|
||||
size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
|
||||
using iterator = reserve_iterator<OutputIt>;
|
||||
@ -1830,7 +1841,7 @@ auto write_float(OutputIt out, const DecimalFP& fp,
|
||||
if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1;
|
||||
if (num_zeros > 0) size += to_unsigned(num_zeros) + 1;
|
||||
}
|
||||
auto grouping = digit_grouping<Char>(loc, fspecs.locale);
|
||||
auto grouping = Grouping(loc, fspecs.locale);
|
||||
size += to_unsigned(grouping.count_separators(significand_size));
|
||||
return write_padded<align::right>(out, specs, size, [&](iterator it) {
|
||||
if (sign) *it++ = detail::sign<Char>(sign);
|
||||
@ -1844,7 +1855,7 @@ auto write_float(OutputIt out, const DecimalFP& fp,
|
||||
// 1234e-2 -> 12.34[0+]
|
||||
int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
|
||||
size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
|
||||
auto grouping = digit_grouping<Char>(loc, fspecs.locale);
|
||||
auto grouping = Grouping(loc, fspecs.locale);
|
||||
size += to_unsigned(grouping.count_separators(significand_size));
|
||||
return write_padded<align::right>(out, specs, size, [&](iterator it) {
|
||||
if (sign) *it++ = detail::sign<Char>(sign);
|
||||
@ -1871,22 +1882,93 @@ auto write_float(OutputIt out, const DecimalFP& fp,
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Char> class fallback_digit_grouping {
|
||||
public:
|
||||
constexpr fallback_digit_grouping(locale_ref, bool) {}
|
||||
|
||||
constexpr Char separator() const { return Char(); }
|
||||
|
||||
constexpr int count_separators(int) const { return 0; }
|
||||
|
||||
template <typename Out, typename C>
|
||||
constexpr Out apply(Out out, basic_string_view<C>) const {
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename DecimalFP, typename Char>
|
||||
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp,
|
||||
const basic_format_specs<Char>& specs,
|
||||
float_specs fspecs, locale_ref loc)
|
||||
-> OutputIt {
|
||||
if (is_constant_evaluated()) {
|
||||
return do_write_float<OutputIt, DecimalFP, Char,
|
||||
fallback_digit_grouping<Char>>(out, fp, specs, fspecs,
|
||||
loc);
|
||||
} else {
|
||||
return do_write_float(out, fp, specs, fspecs, loc);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
FMT_CONSTEXPR20 bool isinf(T value) {
|
||||
if (is_constant_evaluated()) {
|
||||
#if defined(__cpp_if_constexpr)
|
||||
if constexpr (std::numeric_limits<double>::is_iec559) {
|
||||
auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
|
||||
constexpr auto significand_bits =
|
||||
dragonbox::float_info<double>::significand_bits;
|
||||
return (bits & exponent_mask<double>()) &&
|
||||
!(bits & ((uint64_t(1) << significand_bits) - 1));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return std::isinf(value);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
FMT_CONSTEXPR20 bool isfinite(T value) {
|
||||
if (is_constant_evaluated()) {
|
||||
#if defined(__cpp_if_constexpr)
|
||||
if constexpr (std::numeric_limits<double>::is_iec559) {
|
||||
auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
|
||||
return (bits & exponent_mask<double>()) != exponent_mask<double>();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return std::isfinite(value);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
|
||||
if (is_constant_evaluated()) {
|
||||
#ifdef __cpp_if_constexpr
|
||||
if constexpr (std::numeric_limits<double>::is_iec559) {
|
||||
auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
|
||||
return (bits & (uint64_t(1) << (num_bits<uint64_t>() - 1))) != 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return std::signbit(value);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
auto write(OutputIt out, T value, basic_format_specs<Char> specs,
|
||||
locale_ref loc = {}) -> OutputIt {
|
||||
FMT_CONSTEXPR20 auto write(OutputIt out, T value,
|
||||
basic_format_specs<Char> specs, locale_ref loc = {})
|
||||
-> OutputIt {
|
||||
if (const_check(!is_supported_floating_point(value))) return out;
|
||||
float_specs fspecs = parse_float_type_spec(specs);
|
||||
fspecs.sign = specs.sign;
|
||||
if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
|
||||
if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit.
|
||||
fspecs.sign = sign::minus;
|
||||
value = -value;
|
||||
} else if (fspecs.sign == sign::minus) {
|
||||
fspecs.sign = sign::none;
|
||||
}
|
||||
|
||||
if (!std::isfinite(value))
|
||||
return write_nonfinite(out, std::isinf(value), specs, fspecs);
|
||||
if (!detail::isfinite(value))
|
||||
return write_nonfinite(out, detail::isinf(value), specs, fspecs);
|
||||
|
||||
if (specs.align == align::numeric && fspecs.sign) {
|
||||
auto it = reserve(out, 1);
|
||||
@ -1922,7 +2004,11 @@ auto write(OutputIt out, T value, basic_format_specs<Char> specs,
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_fast_float<T>::value)>
|
||||
auto write(OutputIt out, T value) -> OutputIt {
|
||||
FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
|
||||
if (is_constant_evaluated()) {
|
||||
return write(out, value, basic_format_specs<Char>());
|
||||
}
|
||||
|
||||
if (const_check(!is_supported_floating_point(value))) return out;
|
||||
|
||||
using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
|
||||
@ -1930,13 +2016,12 @@ auto write(OutputIt out, T value) -> OutputIt {
|
||||
auto bits = bit_cast<uint>(value);
|
||||
|
||||
auto fspecs = float_specs();
|
||||
auto sign_bit = bits & (uint(1) << (num_bits<uint>() - 1));
|
||||
if (sign_bit != 0) {
|
||||
if (detail::signbit(value)) {
|
||||
fspecs.sign = sign::minus;
|
||||
value = -value;
|
||||
}
|
||||
|
||||
static const auto specs = basic_format_specs<Char>();
|
||||
constexpr auto specs = basic_format_specs<Char>();
|
||||
uint mask = exponent_mask<floaty>();
|
||||
if ((bits & mask) == mask)
|
||||
return write_nonfinite(out, std::isinf(value), specs, fspecs);
|
||||
|
@ -84,6 +84,11 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
|
||||
endif ()
|
||||
add_fmt_test(ostream-test)
|
||||
add_fmt_test(compile-test)
|
||||
add_fmt_test(compile-fp-test HEADER_ONLY)
|
||||
if (MSVC)
|
||||
# Without this option, MSVC returns 199711L for the __cplusplus macro.
|
||||
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
|
||||
endif()
|
||||
add_fmt_test(printf-test)
|
||||
add_fmt_test(ranges-test ranges-odr-test.cc)
|
||||
add_fmt_test(scan-test)
|
||||
|
62
test/compile-fp-test.cc
Normal file
62
test/compile-fp-test.cc
Normal file
@ -0,0 +1,62 @@
|
||||
// Formatting library for C++ - formatting library tests
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/compile.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \
|
||||
defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \
|
||||
defined(__cpp_constexpr_dynamic_alloc) && \
|
||||
__cpp_constexpr_dynamic_alloc >= 201907 && __cplusplus >= 202002L
|
||||
template <size_t max_string_length, typename Char = char> struct test_string {
|
||||
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
|
||||
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
|
||||
}
|
||||
Char buffer[max_string_length]{};
|
||||
};
|
||||
|
||||
template <size_t max_string_length, typename Char = char, typename... Args>
|
||||
consteval auto test_format(auto format, const Args&... args) {
|
||||
test_string<max_string_length, Char> string{};
|
||||
fmt::format_to(string.buffer, format, args...);
|
||||
return string;
|
||||
}
|
||||
|
||||
TEST(compile_time_formatting_test, floating_point) {
|
||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f));
|
||||
EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f));
|
||||
|
||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0));
|
||||
EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0));
|
||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0));
|
||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65));
|
||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65));
|
||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65));
|
||||
EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6));
|
||||
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65));
|
||||
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65));
|
||||
|
||||
EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65));
|
||||
EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65));
|
||||
EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65));
|
||||
EXPECT_EQ("9223372036854775808.000000",
|
||||
test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0));
|
||||
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan));
|
||||
EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan));
|
||||
if (std::signbit(-nan))
|
||||
EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan));
|
||||
else
|
||||
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
||||
|
||||
constexpr double inf = std::numeric_limits<double>::infinity();
|
||||
EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf));
|
||||
EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf));
|
||||
EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf));
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user