summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--absl/numeric/int128_no_intrinsic.inc12
-rw-r--r--absl/numeric/int128_test.cc18
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));