Add support for 96-bit long double

This commit is contained in:
Victor Zverovich
2022-02-18 17:59:02 -08:00
parent 2c8cd2db34
commit a0b43bfae2
4 changed files with 36 additions and 38 deletions

View File

@ -237,11 +237,7 @@ struct fp {
template <typename Float> explicit FMT_CONSTEXPR fp(Float n) { assign(n); }
template <typename Float>
using is_supported =
bool_constant<sizeof(Float) == sizeof(uint32_t) ||
sizeof(Float) == sizeof(uint64_t) ||
(sizeof(Float) == sizeof(uint128_t) &&
std::numeric_limits<Float>::digits == 64)>;
using is_supported = bool_constant<std::numeric_limits<Float>::digits <= 64>;
// Assigns d to this and return true iff predecessor is closer than successor.
template <typename Float, FMT_ENABLE_IF(is_supported<Float>::value)>

View File

@ -288,9 +288,8 @@ template <typename Streambuf> class formatbuf : public Streambuf {
};
// Implementation of std::bit_cast for pre-C++20.
template <typename To, typename From>
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
static_assert(sizeof(To) == sizeof(From), "size mismatch");
#ifdef __cpp_lib_bit_cast
if (is_constant_evaluated()) return std::bit_cast<To>(from);
#endif
@ -369,28 +368,10 @@ using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
#ifdef UINTPTR_MAX
using uintptr_t = ::uintptr_t;
inline auto to_uintptr(const void* p) -> uintptr_t {
return bit_cast<uintptr_t>(p);
}
#else
using uintptr_t = uint128_t;
#endif
// A fallback for systems that lack uintptr_t.
template <typename T = uint128_t> inline auto to_uintptr(const void* p) -> T {
constexpr auto size = static_cast<int>(sizeof(void*));
struct data_t {
unsigned char value[size];
} data = bit_cast<data_t>(p);
auto result = T();
if (const_check(is_big_endian())) {
for (int i = 0; i < size; ++i) result = (result << 8) | data.value[i];
} else {
for (int i = size - 1; i >= 0; --i) result = (result << 8) | data.value[i];
}
return result;
}
// Returns the largest possible value for type T. Same as
// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
template <typename T> constexpr auto max_value() -> T {
@ -403,6 +384,25 @@ template <typename T> constexpr auto num_bits() -> int {
template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
template <> constexpr auto num_bits<uint128_t>() -> int { return 128; }
// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
// and 128-bit pointers to uint128_fallback.
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
inline auto bit_cast(const From& from) -> To {
constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
struct data_t {
unsigned value[size];
} data = bit_cast<data_t>(from);
auto result = To();
if (const_check(is_big_endian())) {
for (int i = 0; i < size; ++i)
result = (result << num_bits<unsigned>()) | data.value[i];
} else {
for (int i = size - 1; i >= 0; --i)
result = (result << num_bits<unsigned>()) | data.value[i];
}
return result;
}
FMT_INLINE void assume(bool condition) {
(void)condition;
#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
@ -1275,7 +1275,7 @@ template <> struct float_info<double> {
template <typename T>
struct float_info<T, enable_if_t<std::is_same<T, long double>::value &&
std::numeric_limits<T>::digits == 64>> {
using carrier_uint = detail::uint128_opt;
using carrier_uint = detail::uint128_t;
static const int significand_bits = 64;
static const int exponent_bits = 15;
};
@ -1911,7 +1911,7 @@ FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
-> OutputIt {
return check_cstring_type_spec(specs.type)
? write(out, basic_string_view<Char>(s), specs, {})
: write_ptr<Char>(out, to_uintptr(s), &specs);
: write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
}
template <typename Char, typename OutputIt>
@ -2394,7 +2394,7 @@ auto write(OutputIt out, const T* value,
const basic_format_specs<Char>& specs = {}, locale_ref = {})
-> OutputIt {
check_pointer_type_spec(specs.type, error_handler());
return write_ptr<Char>(out, to_uintptr(value), &specs);
return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
}
// A write overload that handles implicit conversions.

View File

@ -340,16 +340,6 @@ TEST(format_impl_test, count_digits) {
test_count_digits<uint64_t>();
}
TEST(format_impl_test, write_uintptr_fallback) {
std::string s;
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::to_uintptr<fmt::detail::uint128_fallback>(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ(s, "0xface");
}
#ifdef _WIN32
# include <windows.h>
#endif

View File

@ -1445,6 +1445,18 @@ TEST(format_test, format_pointer) {
EXPECT_EQ("0x0", fmt::format("{}", nullptr));
}
TEST(format_test, write_uintptr_fallback) {
// Test that formatting a pointer by converting it to uint128_fallback works.
// This is needed to support systems without uintptr_t.
auto s = std::string();
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::bit_cast<fmt::detail::uint128_fallback>(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ(s, "0xface");
}
enum class color { red, green, blue };
TEST(format_test, format_enum_class) {