authorgravatar for jonathan.haehne@hotmail.comTau <jonathan.haehne@hotmail.com> 2024-07-11 13:57:37+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-12 00:46:03-07:00
log2b99b0428500ef880bc52e9f814f970e5fa29a7d
tree1a6702df8b6ca91262e13b1e0cbbd5f7bc6232a1
parent3ad81c40c01649551b4ad3d2c450d8b5f7934362

Fix right shift on negative BigInts

Closes #17662.

2 files changed, 40 insertions(+), 6 deletions(-)

lib/std/math/big/int.zig+23-6
...@@ -1173,7 +1173,9 @@ pub const Mutable = struct {...@@ -1173,7 +1173,9 @@ pub const Mutable = struct {
1173 /// Asserts there is enough memory to fit the result. The upper bound Limb count is1173 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
1174 /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`.1174 /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`.
1175 pub fn shiftRight(r: *Mutable, a: Const, shift: usize) void {1175 pub fn shiftRight(r: *Mutable, a: Const, shift: usize) void {
1176 if (a.limbs.len <= shift / limb_bits) {1176 const full_limbs_shifted_out = shift / limb_bits;
1177 const remaining_bits_shifted_out = shift % limb_bits;
1178 if (a.limbs.len <= full_limbs_shifted_out) {
1177 // Shifting negative numbers converges to -1 instead of 01179 // Shifting negative numbers converges to -1 instead of 0
1178 if (a.positive) {1180 if (a.positive) {
1179 r.len = 1;1181 r.len = 1;
...@@ -1186,14 +1188,29 @@ pub const Mutable = struct {...@@ -1186,14 +1188,29 @@ pub const Mutable = struct {
1186 }1188 }
1187 return;1189 return;
1188 }1190 }
1191 const nonzero_negative_shiftout = if (a.positive) false else nonzero: {
1192 for (a.limbs[0..full_limbs_shifted_out]) |x| {
1193 if (x != 0)
1194 break :nonzero true;
1195 }
1196 if (remaining_bits_shifted_out == 0)
1197 break :nonzero false;
1198 const not_covered: Log2Limb = @intCast(limb_bits - remaining_bits_shifted_out);
1199 break :nonzero a.limbs[full_limbs_shifted_out] << not_covered != 0;
1200 };
11891201
1190 llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift);1202 llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift);
1191 r.normalize(a.limbs.len - (shift / limb_bits));1203
1192 r.positive = a.positive;1204 r.len = a.limbs.len - full_limbs_shifted_out;
1193 // Shifting negative numbers converges to -1 instead of 01205 if (nonzero_negative_shiftout) {
1194 if (!r.positive and r.len == 1 and r.limbs[0] == 0) {1206 if (full_limbs_shifted_out > 0) {
1195 r.limbs[0] = 1;1207 r.limbs[a.limbs.len - full_limbs_shifted_out] = 0;
1208 r.len += 1;
1209 }
1210 r.addScalar(r.toConst(), -1);
1196 }1211 }
1212 r.normalize(r.len);
1213 r.positive = a.positive;
1197 }1214 }
11981215
1199 /// r = ~a under 2s complement wrapping semantics.1216 /// r = ~a under 2s complement wrapping semantics.
lib/std/math/big/int_test.zig+17
...@@ -2066,6 +2066,23 @@ test "shift-right negative" {...@@ -2066,6 +2066,23 @@ test "shift-right negative" {
2066 defer arg3.deinit();2066 defer arg3.deinit();
2067 try a.shiftRight(&arg3, 1232);2067 try a.shiftRight(&arg3, 1232);
2068 try testing.expect((try a.to(i32)) == -1); // -10 >> 1232 == -12068 try testing.expect((try a.to(i32)) == -1); // -10 >> 1232 == -1
2069
2070 var arg4 = try Managed.initSet(testing.allocator, -5);
2071 defer arg4.deinit();
2072 try a.shiftRight(&arg4, 2);
2073 try testing.expect(try a.to(i32) == -2); // -5 >> 2 == -2
2074
2075 var arg5 = try Managed.initSet(testing.allocator, -0xffff0000eeee1111dddd2222cccc3333);
2076 defer arg5.deinit();
2077 try a.shiftRight(&arg5, 67);
2078 try testing.expect(try a.to(i64) == -0x1fffe0001dddc223);
2079
2080 var arg6 = try Managed.initSet(testing.allocator, -0x1ffffffffffffffff);
2081 defer arg6.deinit();
2082 try a.shiftRight(&arg6, 1);
2083 try a.shiftRight(&a, 1);
2084 a.setSign(true);
2085 try testing.expect(try a.to(u64) == 0x8000000000000000);
2069}2086}
20702087
2071test "sat shift-left simple unsigned" {2088test "sat shift-left simple unsigned" {