From 5f8eb5cbf8523c49f0af6a841a2af3adabbba8cb Mon Sep 17 00:00:00 2001 From: Yves Delley Date: Wed, 6 Nov 2024 17:04:50 +0100 Subject: [PATCH] made hi_ and lo_ private members of double_width_int --- src/core/include/mp-units/bits/fixed_point.h | 90 ++++++++++++-------- test/runtime/fixed_point_test.cpp | 25 +++--- 2 files changed, 67 insertions(+), 48 deletions(-) diff --git a/src/core/include/mp-units/bits/fixed_point.h b/src/core/include/mp-units/bits/fixed_point.h index 362ada46c..f0ab86188 100644 --- a/src/core/include/mp-units/bits/fixed_point.h +++ b/src/core/include/mp-units/bits/fixed_point.h @@ -40,11 +40,15 @@ import std; namespace mp_units::detail { +template +constexpr std::size_t integer_rep_width_v = std::numeric_limits>::digits; + + // this class synthesizes a double-width integer from two base-width integers. template struct double_width_int { static constexpr bool is_signed = std::is_signed_v; - static constexpr std::size_t base_width = std::numeric_limits>::digits; + static constexpr std::size_t base_width = integer_rep_width_v; static constexpr std::size_t width = 2 * base_width; using Th = T; @@ -52,34 +56,49 @@ struct double_width_int { constexpr double_width_int() = default; - constexpr double_width_int(Th hi_, Tl lo_) : hi(hi_), lo(lo_) {} +private: + constexpr double_width_int(Th hi, Tl lo) : hi_(hi), lo_(lo) {} + + template + friend struct double_width_int; + +public: + static constexpr double_width_int from_hi_lo(Th hi, Tl lo) { return {hi, lo}; } explicit constexpr double_width_int(long double v) { constexpr auto scale = int_power(2, base_width); constexpr auto iscale = 1.l / scale; auto scaled = v * iscale; - hi = static_cast(scaled); - auto resid = (scaled - static_cast(hi)); + hi_ = static_cast(scaled); + auto resid = (scaled - static_cast(hi_)); if (resid < 0) { - --hi; + --hi_; resid += 1; } - lo = static_cast(resid * scale); + lo_ = static_cast(resid * scale); } template requires(is_signed || !std::is_signed_v) explicit(false) constexpr double_width_int(U v) { if constexpr (is_signed) { - hi = v < 0 ? Th{-1} : Th{0}; + hi_ = v < 0 ? Th{-1} : Th{0}; } else { - hi = 0; + hi_ = 0; } - lo = static_cast(v); + lo_ = static_cast(v); } - explicit constexpr operator Th() const { return static_cast(lo); } + template + explicit constexpr operator U() const + { + if constexpr (integer_rep_width_v > base_width) { + return (static_cast(hi_) << base_width) + static_cast(lo_); + } else { + return static_cast(lo_); + } + } [[nodiscard]] constexpr auto operator<=>(const double_width_int&) const = default; @@ -108,10 +127,10 @@ struct double_width_int { [[nodiscard]] friend constexpr auto operator*(const double_width_int& lhs, Rhs rhs) { using RT = std::conditional_t, std::make_signed_t, Tl>; - auto lo_prod = double_width_int::wide_product_of(rhs, lhs.lo); + auto lo_prod = double_width_int::wide_product_of(rhs, lhs.lo_); // Normal C++ rules; with respect to signedness, the wider type always wins. using ret_t = double_width_int; - return ret_t{static_cast(lo_prod.hi) + lhs.hi * static_cast(rhs), lo_prod.lo}; + return ret_t{static_cast(lo_prod.hi_) + lhs.hi_ * static_cast(rhs), lo_prod.lo_}; } template requires(std::numeric_limits::digits <= base_width) @@ -132,19 +151,19 @@ struct double_width_int { return lhs / static_cast(rhs); } } else if constexpr (is_signed) { - if (lhs.hi < 0) { + if (lhs.hi_ < 0) { return -((-lhs) / rhs); } else { using unsigned_t = double_width_int; - auto tmp = unsigned_t{static_cast(lhs.hi), lhs.lo} / rhs; - return ret_t{static_cast(tmp.hi), tmp.lo}; + auto tmp = unsigned_t{static_cast(lhs.hi_), lhs.lo_} / rhs; + return ret_t{static_cast(tmp.hi_), tmp.lo_}; } } else { - Th res_hi = lhs.hi / rhs; + Th res_hi = lhs.hi_ / rhs; // unfortunately, wide division is hard: https://en.wikipedia.org/wiki/Division_algorithm. // Here, we just provide a somewhat naive implementation of long division. - Tl rem_hi = lhs.hi % rhs; - Tl rem_lo = lhs.lo; + Tl rem_hi = lhs.hi_ % rhs; + Tl rem_lo = lhs.lo_; Tl res_lo = 0; for (std::size_t i = 0; i < base_width; ++i) { // shift in one bit @@ -165,14 +184,14 @@ struct double_width_int { requires(std::numeric_limits::digits <= base_width) [[nodiscard]] friend constexpr double_width_int operator+(const double_width_int& lhs, Rhs rhs) { - Th rhi = lhs.hi; - Tl rlo = lhs.lo; + Th rhi = lhs.hi_; + Tl rlo = lhs.lo_; if constexpr (std::is_signed_v) { // sign extension; no matter if lhs is signed, negative rhs sign extend if (rhs < 0) --rhi; } rlo += static_cast(rhs); - if (rlo < lhs.lo) { + if (rlo < lhs.lo_) { // carry bit ++rhi; } @@ -187,14 +206,14 @@ struct double_width_int { requires(std::numeric_limits::digits <= base_width) [[nodiscard]] friend constexpr double_width_int operator-(const double_width_int& lhs, Rhs rhs) { - Th rhi = lhs.hi; - Tl rlo = lhs.lo; + Th rhi = lhs.hi_; + Tl rlo = lhs.lo_; if constexpr (std::is_signed_v) { // sign extension; no matter if lhs is signed, negative rhs sign extend if (rhs < 0) ++rhi; } rlo -= static_cast(rhs); - if (rlo > lhs.lo) { + if (rlo > lhs.lo_) { // carry bit --rhi; } @@ -210,39 +229,40 @@ struct double_width_int { // sign extension; no matter if rhs is signed, negative lhs sign extend if (lhs < 0) --rhi; } - rhi -= rhs.hi; - if (rhs.lo > rlo) { + rhi -= rhs.hi_; + if (rhs.lo_ > rlo) { // carry bit --rhi; } - rlo -= rhs.lo; + rlo -= rhs.lo_; return {rhi, rlo}; } [[nodiscard]] constexpr double_width_int operator-() const { - return {(lo > 0 ? static_cast(-1) : Th{0}) - hi, -lo}; + return {(lo_ > 0 ? static_cast(-1) : Th{0}) - hi_, -lo_}; } [[nodiscard]] constexpr double_width_int operator>>(unsigned n) const { if (n >= base_width) { - return {static_cast(hi < 0 ? -1 : 0), static_cast(hi >> (n - base_width))}; + return {static_cast(hi_ < 0 ? -1 : 0), static_cast(hi_ >> (n - base_width))}; } - return {hi >> n, (static_cast(hi) << (base_width - n)) | (lo >> n)}; + return {hi_ >> n, (static_cast(hi_) << (base_width - n)) | (lo_ >> n)}; } [[nodiscard]] constexpr double_width_int operator<<(unsigned n) const { if (n >= base_width) { - return {static_cast(lo << (n - base_width)), 0}; + return {static_cast(lo_ << (n - base_width)), 0}; } - return {(hi << n) + static_cast(lo >> (base_width - n)), lo << n}; + return {(hi_ << n) + static_cast(lo_ >> (base_width - n)), lo_ << n}; } static constexpr double_width_int max() { return {std::numeric_limits::max(), std::numeric_limits::max()}; } - Th hi; - Tl lo; +private: + Th hi_; + Tl lo_; }; #if defined(__SIZEOF_INT128__) @@ -258,8 +278,6 @@ using uint128_t = double_width_int; constexpr std::size_t max_native_width = 64; #endif -template -constexpr std::size_t integer_rep_width_v = std::numeric_limits>::digits; template constexpr std::size_t integer_rep_width_v> = double_width_int::width; diff --git a/test/runtime/fixed_point_test.cpp b/test/runtime/fixed_point_test.cpp index 2aa621984..c24928fa2 100644 --- a/test/runtime/fixed_point_test.cpp +++ b/test/runtime/fixed_point_test.cpp @@ -93,10 +93,11 @@ auto combine_bits(Hi hi, Lo lo) template void check(double_width_int value, V&& visitor) { - auto as_standard_int = combine_bits(value.hi, value.lo); + using DT = double_width_int_t; + auto as_standard_int = static_cast
(value); auto expected = visitor(as_standard_int); auto actual = visitor(value); - auto actual_as_standard = combine_bits(actual.hi, actual.lo); + auto actual_as_standard = static_cast
(actual); REQUIRE(actual_as_standard == expected); } @@ -133,7 +134,7 @@ TEST_CASE("double_width_int addition and subtraction", "[double_width_int]") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); - double_width_int lhs{lhi, llo}; + auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [&](auto v) { return v + rhs; }); check(lhs, [&](auto v) { return v - rhs; }); check(lhs, [&](auto v) { return rhs - v; }); @@ -143,7 +144,7 @@ TEST_CASE("double_width_int addition and subtraction", "[double_width_int]") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); - double_width_int lhs{lhi, llo}; + auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [&](auto v) { return v + rhs; }); check(lhs, [&](auto v) { return v - rhs; }); check(lhs, [&](auto v) { return rhs - v; }); @@ -153,7 +154,7 @@ TEST_CASE("double_width_int addition and subtraction", "[double_width_int]") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); - double_width_int lhs{lhi, llo}; + auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [&](auto v) { return v + rhs; }); check(lhs, [&](auto v) { return v - rhs; }); check(lhs, [&](auto v) { return rhs - v; }); @@ -163,7 +164,7 @@ TEST_CASE("double_width_int addition and subtraction", "[double_width_int]") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); - double_width_int lhs{lhi, llo}; + auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [&](auto v) { return v + rhs; }); check(lhs, [&](auto v) { return v - rhs; }); check(lhs, [&](auto v) { return rhs - v; }); @@ -179,7 +180,7 @@ TEST_CASE("double_width_int multiplication", "[double_width_int]") CAPTURE(lhs, rhs); u64 expected = u64{lhs} * u64{rhs}; auto actual = double_width_int::wide_product_of(lhs, rhs); - u64 actual_as_std = combine_bits(actual.hi, actual.lo); + auto actual_as_std = static_cast(actual); REQUIRE(actual_as_std == expected); } } @@ -189,7 +190,7 @@ TEST_CASE("double_width_int multiplication", "[double_width_int]") CAPTURE(lhs, rhs); i64 expected = i64{lhs} * i64{rhs}; auto actual = double_width_int::wide_product_of(lhs, rhs); - i64 actual_as_std = combine_bits(actual.hi, actual.lo); + auto actual_as_std = static_cast(actual); REQUIRE(actual_as_std == expected); } } @@ -197,7 +198,7 @@ TEST_CASE("double_width_int multiplication", "[double_width_int]") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); - double_width_int lhs{lhi, llo}; + auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [&](auto v) { return v * rhs; }); } } @@ -205,7 +206,7 @@ TEST_CASE("double_width_int multiplication", "[double_width_int]") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); - double_width_int lhs{lhi, llo}; + auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [&](auto v) { return v * rhs; }); } } @@ -213,7 +214,7 @@ TEST_CASE("double_width_int multiplication", "[double_width_int]") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); - double_width_int lhs{lhi, llo}; + auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [&](auto v) { return v * rhs; }); } } @@ -221,7 +222,7 @@ TEST_CASE("double_width_int multiplication", "[double_width_int]") { for (auto [lhi, llo, rhs] : cartesian_product(test_values(), test_values(), test_values())) { CAPTURE(lhi, llo, rhs); - double_width_int lhs{lhi, llo}; + auto lhs = double_width_int::from_hi_lo(lhi, llo); check(lhs, [&](auto v) { return v * rhs; }); } }