uint128_fallback -> uint128

This commit is contained in:
Victor Zverovich 2026-02-02 19:13:14 -08:00
parent 6322cf0520
commit b98926b73b
4 changed files with 65 additions and 78 deletions

View File

@ -404,9 +404,9 @@ inline auto map(native_uint128 x) -> native_uint128 { return x; }
# define FMT_USE_INT128 0
#endif
#if !FMT_USE_INT128
enum class native_int128 {}; // A fallback to reduce conditional compilation.
// Fallbacks to reduce conditional compilation and SFINAE.
enum class native_int128 {};
enum class native_uint128 {};
// Reduce template instantiations.
inline auto map(native_int128) -> monostate { return {}; }
inline auto map(native_uint128) -> monostate { return {}; }
#endif

View File

@ -209,10 +209,9 @@ inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t {
// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept
-> uint128_fallback {
inline auto umul192_lower128(uint64_t x, uint128 y) noexcept -> uint128 {
uint64_t high = x * y.high();
uint128_fallback high_low = umul128(x, y.low());
uint128 high_low = umul128(x, y.low());
return {high + high_low.high(), high_low.low()};
}
@ -380,13 +379,13 @@ template <> struct cache_accessor<float> {
template <> struct cache_accessor<double> {
using carrier_uint = float_info<double>::carrier_uint;
using cache_entry_type = uint128_fallback;
using cache_entry_type = uint128;
static auto get_cached_power(int k) noexcept -> uint128_fallback {
static auto get_cached_power(int k) noexcept -> uint128 {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
static constexpr uint128_fallback pow10_significands[] = {
static constexpr uint128 pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
@ -1072,7 +1071,7 @@ template <> struct cache_accessor<double> {
int offset = k - kb;
// Get base cache.
uint128_fallback base_cache = pow10_significands[cache_index];
uint128 base_cache = pow10_significands[cache_index];
if (offset == 0) return base_cache;
// Compute the required amount of bit-shift.
@ -1081,17 +1080,16 @@ template <> struct cache_accessor<double> {
// Try to recover the real cache.
uint64_t pow5 = powers_of_5_64[offset];
uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
uint128_fallback middle_low = umul128(base_cache.low(), pow5);
uint128 recovered_cache = umul128(base_cache.high(), pow5);
uint128 middle_low = umul128(base_cache.low(), pow5);
recovered_cache += middle_low.high();
uint64_t high_to_middle = recovered_cache.high() << (64 - alpha);
uint64_t middle_to_low = recovered_cache.low() << (64 - alpha);
recovered_cache =
uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle,
((middle_low.low() >> alpha) | middle_to_low)};
recovered_cache = uint128{(recovered_cache.low() >> alpha) | high_to_middle,
((middle_low.low() >> alpha) | middle_to_low)};
FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
return {recovered_cache.high(), recovered_cache.low() + 1};
#endif
@ -1152,7 +1150,7 @@ template <> struct cache_accessor<double> {
}
};
FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback {
FMT_FUNC auto get_cached_power(int k) noexcept -> uint128 {
return cache_accessor<double>::get_cached_power(k);
}

View File

@ -291,13 +291,13 @@ inline auto is_big_endian() -> bool {
#endif
}
class uint128_fallback {
class uint128 {
private:
uint64_t lo_, hi_;
public:
constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
constexpr uint128(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
constexpr uint128(uint64_t value = 0) : lo_(value), hi_(0) {}
constexpr auto high() const noexcept -> uint64_t { return hi_; }
constexpr auto low() const noexcept -> uint64_t { return lo_; }
@ -307,77 +307,69 @@ class uint128_fallback {
return static_cast<T>(lo_);
}
friend constexpr auto operator==(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator==(const uint128& lhs, const uint128& rhs)
-> bool {
return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
}
friend constexpr auto operator!=(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator!=(const uint128& lhs, const uint128& rhs)
-> bool {
return !(lhs == rhs);
}
friend constexpr auto operator>(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator>(const uint128& lhs, const uint128& rhs)
-> bool {
return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
}
friend constexpr auto operator|(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
friend constexpr auto operator|(const uint128& lhs, const uint128& rhs)
-> uint128 {
return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
}
friend constexpr auto operator&(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
friend constexpr auto operator&(const uint128& lhs, const uint128& rhs)
-> uint128 {
return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
}
friend constexpr auto operator~(const uint128_fallback& n)
-> uint128_fallback {
return {~n.hi_, ~n.lo_};
}
friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
auto result = uint128_fallback(lhs);
friend FMT_CONSTEXPR auto operator+(const uint128& lhs, const uint128& rhs)
-> uint128 {
auto result = uint128(lhs);
result += rhs;
return result;
}
friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs)
-> uint128_fallback {
friend FMT_CONSTEXPR auto operator*(const uint128& lhs, uint32_t rhs)
-> uint128 {
FMT_ASSERT(lhs.hi_ == 0, "");
uint64_t hi = (lhs.lo_ >> 32) * rhs;
uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;
uint64_t new_lo = (hi << 32) + lo;
return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
}
friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs)
-> uint128_fallback {
friend constexpr auto operator-(const uint128& lhs, uint64_t rhs) -> uint128 {
return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
}
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128 {
if (shift == 64) return {0, hi_};
if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
if (shift > 64) return uint128(0, hi_) >> (shift - 64);
return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
}
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128 {
if (shift == 64) return {lo_, 0};
if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
if (shift > 64) return uint128(lo_, 0) << (shift - 64);
return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
}
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128& {
return *this = *this >> shift;
}
FMT_CONSTEXPR void operator+=(uint128_fallback n) {
FMT_CONSTEXPR void operator+=(uint128 n) {
uint64_t new_lo = lo_ + n.lo_;
uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
FMT_ASSERT(new_hi >= hi_, "");
lo_ = new_lo;
hi_ = new_hi;
}
FMT_CONSTEXPR void operator&=(uint128_fallback n) {
FMT_CONSTEXPR void operator&=(uint128 n) {
lo_ &= n.lo_;
hi_ &= n.hi_;
}
FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& {
FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128& {
if (is_constant_evaluated()) {
lo_ += n;
hi_ += (lo_ < n ? 1 : 0);
@ -403,8 +395,7 @@ class uint128_fallback {
}
};
using uint128_t =
conditional_t<FMT_USE_INT128, native_uint128, uint128_fallback>;
using uint128_t = conditional_t<FMT_USE_INT128, native_uint128, uint128>;
#ifdef UINTPTR_MAX
using uintptr_t = ::uintptr_t;
@ -423,10 +414,10 @@ template <typename T> constexpr auto num_bits() -> int {
// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
template <> constexpr auto num_bits<native_int128>() -> int { return 128; }
template <> constexpr auto num_bits<native_uint128>() -> int { return 128; }
template <> constexpr auto num_bits<uint128_fallback>() -> int { return 128; }
template <> constexpr auto num_bits<uint128>() -> int { return 128; }
// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
// and 128-bit pointers to uint128_fallback.
// and 128-bit pointers to uint128.
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 short));
@ -1472,7 +1463,7 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
};
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback {
FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128 {
#if FMT_USE_INT128
auto p = static_cast<native_uint128>(x) * static_cast<native_uint128>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
@ -1528,14 +1519,13 @@ inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t {
// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept
-> uint128_fallback {
uint128_fallback r = umul128(x, y.high());
inline auto umul192_upper128(uint64_t x, uint128 y) noexcept -> uint128 {
uint128 r = umul128(x, y.high());
r += umul128_upper64(x, y.low());
return r;
}
FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback;
FMT_API auto get_cached_power(int k) noexcept -> uint128;
// Type-specific information that Dragonbox uses.
template <typename T, typename Enable = void> struct float_info;

View File

@ -43,7 +43,7 @@ using fmt::memory_buffer;
using fmt::runtime;
using fmt::string_view;
using fmt::detail::max_value;
using fmt::detail::uint128_fallback;
using fmt::detail::uint128;
using testing::Return;
using testing::StrictMock;
@ -55,15 +55,15 @@ static_assert(std::output_iterator<fmt::appender, char>);
enum { buffer_size = 256 };
TEST(uint128_test, ctor) {
auto n = uint128_fallback();
auto n = uint128();
EXPECT_EQ(n, 0);
n = uint128_fallback(42);
n = uint128(42);
EXPECT_EQ(n, 42);
EXPECT_EQ(static_cast<uint64_t>(n), 42);
}
TEST(uint128_test, shift) {
auto n = uint128_fallback(42);
auto n = uint128(42);
n = n << 64;
EXPECT_EQ(static_cast<uint64_t>(n), 0);
n = n >> 64;
@ -73,26 +73,26 @@ TEST(uint128_test, shift) {
EXPECT_EQ(static_cast<uint64_t>(n), 0x8000000000000000);
n = n >> 62;
EXPECT_EQ(static_cast<uint64_t>(n), 42);
EXPECT_EQ(uint128_fallback(1) << 112, uint128_fallback(0x1000000000000, 0));
EXPECT_EQ(uint128_fallback(0x1000000000000, 0) >> 112, uint128_fallback(1));
EXPECT_EQ(uint128(1) << 112, uint128(0x1000000000000, 0));
EXPECT_EQ(uint128(0x1000000000000, 0) >> 112, uint128(1));
}
TEST(uint128_test, minus) {
auto n = uint128_fallback(42);
auto n = uint128(42);
EXPECT_EQ(n - 2, 40);
}
TEST(uint128_test, plus_assign) {
auto n = uint128_fallback(32);
n += uint128_fallback(10);
auto n = uint128(32);
n += uint128(10);
EXPECT_EQ(n, 42);
n = uint128_fallback(max_value<uint64_t>());
n += uint128_fallback(1);
EXPECT_EQ(n, uint128_fallback(1) << 64);
n = uint128(max_value<uint64_t>());
n += uint128(1);
EXPECT_EQ(n, uint128(1) << 64);
}
TEST(uint128_test, multiply) {
auto n = uint128_fallback(2251799813685247);
auto n = uint128(2251799813685247);
n = n * 3611864890;
EXPECT_EQ(static_cast<uint64_t>(n >> 64), 440901);
}
@ -1723,14 +1723,13 @@ TEST(format_test, format_pointer) {
}
TEST(format_test, write_uintptr_fallback) {
// Test that formatting a pointer by converting it to uint128_fallback works.
// Test that formatting a pointer by converting it to uint128 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);
fmt::detail::write_ptr<char>(std::back_inserter(s),
fmt::detail::bit_cast<fmt::detail::uint128>(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ(s, "0xface");
}