diff options
-rw-r--r-- | absl/numeric/int128_no_intrinsic.inc | 12 | ||||
-rw-r--r-- | absl/numeric/int128_test.cc | 18 |
2 files changed, 27 insertions, 3 deletions
diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc index 66f6809f..8834804c 100644 --- a/absl/numeric/int128_no_intrinsic.inc +++ b/absl/numeric/int128_no_intrinsic.inc @@ -279,7 +279,7 @@ constexpr int128 operator^(int128 lhs, int128 rhs) { } constexpr int128 operator<<(int128 lhs, int amount) { - // uint64_t shifts of >= 64 are undefined, so we need some special-casing. + // int64_t shifts of >= 64 are undefined, so we need some special-casing. return amount >= 64 ? MakeInt128( static_cast<int64_t>(Int128Low64(lhs) << (amount - 64)), 0) @@ -292,10 +292,16 @@ constexpr int128 operator<<(int128 lhs, int amount) { } constexpr int128 operator>>(int128 lhs, int amount) { - // uint64_t shifts of >= 64 are undefined, so we need some special-casing. + // int64_t shifts of >= 64 are undefined, so we need some special-casing. + // The (Int128High64(lhs) >> 32) >> 32 "trick" causes the the most significant + // int64 to be inititialized with all zeros or all ones correctly. It takes + // into account whether the number is negative or positive, and whether the + // current architecture does arithmetic or logical right shifts for negative + // numbers. return amount >= 64 ? MakeInt128( - 0, static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64))) + (Int128High64(lhs) >> 32) >> 32, + static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64))) : amount == 0 ? lhs : MakeInt128(Int128High64(lhs) >> amount, diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc index c445d89a..dd9425d7 100644 --- a/absl/numeric/int128_test.cc +++ b/absl/numeric/int128_test.cc @@ -239,6 +239,24 @@ TEST(Uint128, AllTests) { EXPECT_EQ(absl::Uint128Max(), absl::kuint128max); } +TEST(Int128, RightShiftOfNegativeNumbers) { + absl::int128 minus_six = -6; + absl::int128 minus_three = -3; + absl::int128 minus_two = -2; + absl::int128 minus_one = -1; + if ((-6 >> 1) == -3) { + // Right shift is arithmetic (sign propagates) + EXPECT_EQ(minus_six >> 1, minus_three); + EXPECT_EQ(minus_six >> 2, minus_two); + EXPECT_EQ(minus_six >> 65, minus_one); + } else { + // Right shift is logical (zeros shifted in at MSB) + EXPECT_EQ(minus_six >> 1, absl::int128(absl::uint128(minus_six) >> 1)); + EXPECT_EQ(minus_six >> 2, absl::int128(absl::uint128(minus_six) >> 2)); + EXPECT_EQ(minus_six >> 65, absl::int128(absl::uint128(minus_six) >> 65)); + } +} + TEST(Uint128, ConversionTests) { EXPECT_TRUE(absl::MakeUint128(1, 0)); |