mirror of
https://github.com/fmtlib/fmt.git
synced 2025-11-28 13:19:48 +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:
@@ -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,8 +1658,9 @@ 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,
|
||||
const float_specs& fspecs) -> OutputIt {
|
||||
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");
|
||||
constexpr size_t str_size = 3;
|
||||
@@ -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,8 +1692,8 @@ 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,
|
||||
int significand_size) -> OutputIt {
|
||||
constexpr auto write_significand(OutputIt out, const char* significand,
|
||||
int significand_size) -> OutputIt {
|
||||
return copy_str<Char>(significand, significand + significand_size, out);
|
||||
}
|
||||
template <typename Char, typename OutputIt, typename UInt>
|
||||
@@ -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,9 +1745,9 @@ inline auto write_significand(OutputIt out, UInt significand,
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
inline auto write_significand(OutputIt out, const char* significand,
|
||||
int significand_size, int integral_size,
|
||||
Char decimal_point) -> OutputIt {
|
||||
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,
|
||||
significand + integral_size, out);
|
||||
if (!decimal_point) return out;
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user