diff --git a/libcxx/include/__bit/rotate.h b/libcxx/include/__bit/rotate.h --- a/libcxx/include/__bit/rotate.h +++ b/libcxx/include/__bit/rotate.h @@ -20,29 +20,30 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template -_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 -_Tp __rotr(_Tp __t, unsigned int __cnt) _NOEXCEPT -{ - static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__rotr requires an unsigned integer type"); - const unsigned int __dig = numeric_limits<_Tp>::digits; - if ((__cnt % __dig) == 0) - return __t; - return (__t >> (__cnt % __dig)) | (__t << (__dig - (__cnt % __dig))); +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __rotr(_Tp __t, int __cnt) _NOEXCEPT { + static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__rotr requires an unsigned integer type"); + const unsigned int __dig = numeric_limits<_Tp>::digits; + if ((__cnt % __dig) == 0) + return __t; + + if (__cnt < 0) { + __cnt *= -1; + return (__t << (__cnt % __dig)) | (__t >> (__dig - (__cnt % __dig))); // rotr with negative __cnt is similar to rotl + } + + return (__t >> (__cnt % __dig)) | (__t << (__dig - (__cnt % __dig))); } #if _LIBCPP_STD_VER >= 20 template <__libcpp_unsigned_integer _Tp> -[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp rotl(_Tp __t, unsigned int __cnt) noexcept { - const unsigned int __dig = numeric_limits<_Tp>::digits; - if ((__cnt % __dig) == 0) - return __t; - return (__t << (__cnt % __dig)) | (__t >> (__dig - (__cnt % __dig))); +[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp rotl(_Tp __t, int __cnt) noexcept { + return std::__rotr(__t, -__cnt); } template <__libcpp_unsigned_integer _Tp> -[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp rotr(_Tp __t, unsigned int __cnt) noexcept { +[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp rotr(_Tp __t, int __cnt) noexcept { return std::__rotr(__t, __cnt); } diff --git a/libcxx/include/bit b/libcxx/include/bit --- a/libcxx/include/bit +++ b/libcxx/include/bit @@ -34,9 +34,9 @@ // [bit.rotate], rotating template - constexpr T rotl(T x, unsigned int s) noexcept; // C++20 + constexpr T rotl(T x, int s) noexcept; // C++20 template - constexpr T rotr(T x, unsigned int s) noexcept; // C++20 + constexpr T rotr(T x, int s) noexcept; // C++20 // [bit.count], counting template diff --git a/libcxx/test/std/numerics/bit/bitops.rot/rotl.pass.cpp b/libcxx/test/std/numerics/bit/bitops.rot/rotl.pass.cpp --- a/libcxx/test/std/numerics/bit/bitops.rot/rotl.pass.cpp +++ b/libcxx/test/std/numerics/bit/bitops.rot/rotl.pass.cpp @@ -31,6 +31,7 @@ ASSERT_SAME_TYPE(decltype(std::rotl(T(), 0)), T); ASSERT_NOEXCEPT(std::rotl(T(), 0)); T max = std::numeric_limits::max(); + T highbit = std::rotr(T(1), 1); assert(std::rotl(T(max - 1), 0) == T(max - 1)); assert(std::rotl(T(max - 1), 1) == T(max - 2)); @@ -41,6 +42,14 @@ assert(std::rotl(T(max - 1), 6) == T(max - 64)); assert(std::rotl(T(max - 1), 7) == T(max - 128)); + assert(std::rotl(T(max - 1), -1) == T(max - highbit)); + assert(std::rotl(T(max - 1), -2) == T(max - (highbit >> 1))); + assert(std::rotl(T(max - 1), -3) == T(max - (highbit >> 2))); + assert(std::rotl(T(max - 1), -4) == T(max - (highbit >> 3))); + assert(std::rotl(T(max - 1), -5) == T(max - (highbit >> 4))); + assert(std::rotl(T(max - 1), -6) == T(max - (highbit >> 5))); + assert(std::rotl(T(max - 1), -7) == T(max - (highbit >> 6))); + assert(std::rotl(T(1), 0) == T(1)); assert(std::rotl(T(1), 1) == T(2)); assert(std::rotl(T(1), 2) == T(4)); @@ -50,6 +59,14 @@ assert(std::rotl(T(1), 6) == T(64)); assert(std::rotl(T(1), 7) == T(128)); + assert(std::rotl(T(128), -1) == T(64)); + assert(std::rotl(T(128), -2) == T(32)); + assert(std::rotl(T(128), -3) == T(16)); + assert(std::rotl(T(128), -4) == T(8)); + assert(std::rotl(T(128), -5) == T(4)); + assert(std::rotl(T(128), -6) == T(2)); + assert(std::rotl(T(128), -7) == T(1)); + #ifndef TEST_HAS_NO_INT128 if constexpr (std::is_same_v) { T val = (T(1) << 63) | (T(1) << 64); @@ -59,6 +76,12 @@ assert(std::rotl(val, 1) == val << 1); assert(std::rotl(val, 127) == val >> 1); assert(std::rotl(T(3), 127) == ((T(1) << 127) | T(1))); + + assert(std::rotl(val, -128) == val); + assert(std::rotl(val, -256) == val); + assert(std::rotl(val, -1) == val >> 1); + assert(std::rotl(val, -127) == val << 1); + assert(std::rotl(T(3), -1) == ((T(1) << 127) | T(1))); } #endif diff --git a/libcxx/test/std/numerics/bit/bitops.rot/rotr.pass.cpp b/libcxx/test/std/numerics/bit/bitops.rot/rotr.pass.cpp --- a/libcxx/test/std/numerics/bit/bitops.rot/rotr.pass.cpp +++ b/libcxx/test/std/numerics/bit/bitops.rot/rotr.pass.cpp @@ -42,6 +42,14 @@ assert(std::rotr(T(max - 1), 6) == T(max - (highbit >> 5))); assert(std::rotr(T(max - 1), 7) == T(max - (highbit >> 6))); + assert(std::rotr(T(max - 1), -1) == T(max - 2)); + assert(std::rotr(T(max - 1), -2) == T(max - 4)); + assert(std::rotr(T(max - 1), -3) == T(max - 8)); + assert(std::rotr(T(max - 1), -4) == T(max - 16)); + assert(std::rotr(T(max - 1), -5) == T(max - 32)); + assert(std::rotr(T(max - 1), -6) == T(max - 64)); + assert(std::rotr(T(max - 1), -7) == T(max - 128)); + assert(std::rotr(T(128), 0) == T(128)); assert(std::rotr(T(128), 1) == T(64)); assert(std::rotr(T(128), 2) == T(32)); @@ -51,6 +59,14 @@ assert(std::rotr(T(128), 6) == T(2)); assert(std::rotr(T(128), 7) == T(1)); + assert(std::rotr(T(1), -1) == T(2)); + assert(std::rotr(T(1), -2) == T(4)); + assert(std::rotr(T(1), -3) == T(8)); + assert(std::rotr(T(1), -4) == T(16)); + assert(std::rotr(T(1), -5) == T(32)); + assert(std::rotr(T(1), -6) == T(64)); + assert(std::rotr(T(1), -7) == T(128)); + #ifndef TEST_HAS_NO_INT128 if constexpr (std::is_same_v) { T val = (T(1) << 63) | (T(1) << 64); @@ -60,6 +76,12 @@ assert(std::rotr(val, 1) == val >> 1); assert(std::rotr(val, 127) == val << 1); assert(std::rotr(T(3), 1) == ((T(1) << 127) | T(1))); + + assert(std::rotr(val, -128) == val); + assert(std::rotr(val, -256) == val); + assert(std::rotr(val, -1) == val << 1); + assert(std::rotr(val, -127) == val >> 1); + assert(std::rotr(T(3), -127) == ((T(1) << 127) | T(1))); } #endif