Skip to content

Commit 8a2541a

Browse files
committed
Right shift of a signed value will sign-extend
1 parent 5faab4a commit 8a2541a

File tree

5 files changed

+74
-28
lines changed

5 files changed

+74
-28
lines changed

sus/num/__private/intrinsics.h

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,6 @@ sus_pure_const sus_always_inline constexpr T unchecked_xor(T x, T y) noexcept {
121121
return static_cast<T>(MathType<T>{x} ^ MathType<T>{y});
122122
}
123123

124-
template <class T>
125-
requires(std::is_integral_v<T> && !std::is_signed_v<T>)
126-
sus_pure_const sus_always_inline constexpr T unchecked_shl(
127-
T x, uint64_t y) noexcept {
128-
return static_cast<T>(MathType<T>{x} << y);
129-
}
130-
131-
template <class T>
132-
requires(std::is_integral_v<T> && !std::is_signed_v<T>)
133-
sus_pure_const sus_always_inline constexpr T unchecked_shr(
134-
T x, uint64_t y) noexcept {
135-
return static_cast<T>(MathType<T>{x} >> y);
136-
}
137-
138124
template <class T>
139125
requires(std::is_integral_v<T>)
140126
sus_pure_const sus_always_inline constexpr uint32_t num_bits() noexcept {
@@ -466,6 +452,28 @@ sus_pure_const sus_always_inline constexpr T min_value() noexcept {
466452
return -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0;
467453
}
468454

455+
template <class T>
456+
requires(std::is_integral_v<T> && !std::is_signed_v<T>)
457+
sus_pure_const sus_always_inline constexpr T unchecked_shl(
458+
T x, uint64_t y) noexcept {
459+
return static_cast<T>(MathType<T>{x} << y);
460+
}
461+
462+
template <class T>
463+
requires(std::is_integral_v<T> && !std::is_signed_v<T>)
464+
sus_pure_const sus_always_inline constexpr T unchecked_shr(
465+
T x, uint64_t y) noexcept {
466+
return static_cast<T>(MathType<T>{x} >> y);
467+
}
468+
469+
template <class T>
470+
requires(std::is_integral_v<T> && std::is_signed_v<T>)
471+
sus_pure_const sus_always_inline constexpr T unchecked_shr(
472+
T x, uint64_t y) noexcept {
473+
// Performs sign extension.
474+
return static_cast<T>(MathType<T>{x} >> y);
475+
}
476+
469477
template <class T>
470478
requires(std::is_integral_v<T> && std::is_unsigned_v<T> &&
471479
::sus::mem::size_of<T>() <= 8)
@@ -1105,9 +1113,8 @@ sus_pure_const inline constexpr OverflowOut<T> shr_with_overflow(
11051113
const bool overflow = shift >= num_bits<T>();
11061114
if (overflow) [[unlikely]]
11071115
shift = shift & (unchecked_sub(num_bits<T>(), uint32_t{1}));
1108-
return OverflowOut sus_clang_bug_56394(<T>){
1109-
.overflow = overflow,
1110-
.value = into_signed(unchecked_shr(into_unsigned(x), shift))};
1116+
return OverflowOut sus_clang_bug_56394(<T>){.overflow = overflow,
1117+
.value = unchecked_shr(x, shift)};
11111118
}
11121119

11131120
template <class T>

sus/num/__private/signed_integer_methods.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,8 @@ constexpr inline void operator<<=(u32 r) & noexcept {
952952
primitive_value = out.value;
953953
}
954954
/// Satisfies the [`ShrAssign<@doc.self>`]($sus::num::ShrAssign) concept.
955+
///
956+
/// Performs sign extension, copying the sign bit to the right if its set.
955957
constexpr inline void operator>>=(u32 r) & noexcept {
956958
const auto out =
957959
__private::shr_with_overflow(primitive_value, r.primitive_value);

sus/num/i16_unittest.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include <bit>
1516
#include <type_traits>
1617

1718
#include "googletest/include/gtest/gtest.h"
@@ -749,4 +750,28 @@ TEST(i16, fmt) {
749750
EXPECT_EQ(fmt::format("{:+#x}", 12345_i16), "+0x3039");
750751
}
751752

753+
TEST(i16, Shr) {
754+
constexpr auto a = (5_i16) >> 1_u32;
755+
EXPECT_EQ(a, 2_i16);
756+
EXPECT_EQ(4_i16 >> 1_u32, 2_i16);
757+
758+
// ** Signed only.
759+
EXPECT_EQ(-4_i16 >> 1_u32,
760+
std::bit_cast<i16>((std::bit_cast<u16>(int16_t{-4}) >> 1u) |
761+
0b1000'0000'0000'0000_u16));
762+
EXPECT_EQ(-1_i16 >> 15_u32, std::bit_cast<i16>(0xffff_u16));
763+
764+
auto x = 4_i16;
765+
x >>= 1_u32;
766+
EXPECT_EQ(x, 2_i16);
767+
768+
// ** Signed only.
769+
x = -4_i16;
770+
x >>= 1_u32;
771+
EXPECT_EQ(x, std::bit_cast<i16>((std::bit_cast<u16>(int16_t{-4}) >> 1u) |
772+
0b1000'0000'0000'0000_u16));
773+
774+
EXPECT_EQ(i16::MIN >> 7u, std::bit_cast<int16_t>(uint16_t{0xff00u}));
775+
}
776+
752777
} // namespace

sus/num/i32_unittest.cc

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include <bit>
1516
#include <sstream>
1617
#include <type_traits>
1718

@@ -1798,8 +1799,9 @@ TEST(i32, Shr) {
17981799

17991800
// ** Signed only.
18001801
EXPECT_EQ(-4_i32 >> 1_u32,
1801-
i32(static_cast<int32_t>(static_cast<uint32_t>(-4) >> 1)));
1802-
EXPECT_EQ(-1_i32 >> 31_u32, 1_i32);
1802+
std::bit_cast<i32>((std::bit_cast<u32>(-4) >> 1u) |
1803+
0b1000'0000'0000'0000'0000'0000'0000'0000));
1804+
EXPECT_EQ(-1_i32 >> 31_u32, std::bit_cast<i32>(0xffffffff));
18031805

18041806
auto x = 4_i32;
18051807
x >>= 1_u32;
@@ -1808,7 +1810,10 @@ TEST(i32, Shr) {
18081810
// ** Signed only.
18091811
x = -4_i32;
18101812
x >>= 1_u32;
1811-
EXPECT_EQ(x, i32(static_cast<int32_t>(static_cast<uint32_t>(-4) >> 1)));
1813+
EXPECT_EQ(x, std::bit_cast<i32>((std::bit_cast<u32>(-4) >> 1u) |
1814+
0b1000'0000'0000'0000'0000'0000'0000'0000));
1815+
1816+
EXPECT_EQ(i32::MIN >> 7u, std::bit_cast<int32_t>(uint32_t{0xff000000u}));
18121817
}
18131818

18141819
TEST(i32DeathTest, ShrOverflow) {
@@ -1845,9 +1850,10 @@ TEST(i32, CheckedShr) {
18451850
EXPECT_EQ((1_i32).checked_shr(64_u32), None);
18461851

18471852
// ** Signed only.
1848-
EXPECT_EQ(
1849-
(-2_i32).checked_shr(1_u32),
1850-
Option<i32>(i32(static_cast<int32_t>(static_cast<uint32_t>(-2) >> 1))));
1853+
EXPECT_EQ((-2_i32).checked_shr(1_u32),
1854+
Option<i32>(std::bit_cast<i32>(
1855+
(std::bit_cast<u32>(-2) >> 1u) |
1856+
0b1000'0000'0000'0000'0000'0000'0000'0000u)));
18511857
EXPECT_EQ((-1_i32).checked_shr(32_u32), None);
18521858
}
18531859

@@ -1862,10 +1868,11 @@ TEST(i32, OverflowingShr) {
18621868
EXPECT_EQ((4_i32).overflowing_shr(33_u32), (Tuple<i32, bool>(2_i32, true)));
18631869

18641870
// ** Signed only.
1865-
EXPECT_EQ(
1866-
(-2_i32).overflowing_shr(1_u32),
1867-
(Tuple<i32, bool>(
1868-
i32(static_cast<int32_t>(static_cast<uint32_t>(-2) >> 1u)), false)));
1871+
EXPECT_EQ((-2_i32).overflowing_shr(1_u32),
1872+
(Tuple<i32, bool>(
1873+
std::bit_cast<i32>((std::bit_cast<u32>(-2) >> 1u) |
1874+
0b1000'0000'0000'0000'0000'0000'0000'0000u),
1875+
false)));
18691876
}
18701877

18711878
TEST(i32, WrappingShr) {
@@ -1880,7 +1887,8 @@ TEST(i32, WrappingShr) {
18801887

18811888
// ** Signed only.
18821889
EXPECT_EQ((-2_i32).wrapping_shr(1_u32),
1883-
i32(static_cast<int32_t>(static_cast<uint32_t>(-2) >> 1u)));
1890+
std::bit_cast<i32>((std::bit_cast<u32>(-2) >> 1u) |
1891+
0b1000'0000'0000'0000'0000'0000'0000'0000u));
18841892
}
18851893

18861894
TEST(i32, Sub) {

sus/num/signed_integer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ constexpr inline P operator<<(P l, U r) noexcept = delete;
195195

196196
/// Satisfies the [`Shr`]($sus::num::Shr) concept for signed primitive integers
197197
/// shifted by [`u64`]($sus::num::u64).
198+
///
199+
/// Performs sign extension, copying the sign bit to the right if its set.
198200
/// #[doc.overloads=signed.prim.>>u64]
199201
template <class P, Integer U>
200202
requires((SignedPrimitiveInteger<P> || SignedPrimitiveEnum<P>) &&
@@ -235,6 +237,8 @@ template <class U>
235237
constexpr inline i8 operator<<(i8 l, U r) noexcept = delete;
236238
/// Satisfies the [`Shr`]($sus::num::Shr) concept for signed integers.
237239
///
240+
/// Performs sign extension, copying the sign bit to the right if its set.
241+
///
238242
/// #[doc.overloads=signedint.>>]
239243
[[nodiscard]] sus_pure_const constexpr inline i8 operator>>(
240244
i8 l, std::convertible_to<u64> auto r) noexcept {

0 commit comments

Comments
 (0)