authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-03-25 18:44:35+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-03-25 18:44:58+01:00
log38ececf0a76867ff0e56f38879bb72344f9f9f17
tree4526350f3c9ec46fca4ef98de2b1b3b6d5c6ab16
parentcb3eec285ff254cf36a1e48af06e764f8a9b23bd
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Merge pull request #23310 from Rexicon226/fix-23309

big.int: return normalized results from `{add,sub}Carry`

4 files changed, 53 insertions(+), 5 deletions(-)

lib/std/math/big/int.zig+15-4
...@@ -76,9 +76,18 @@ pub fn calcSqrtLimbsBufferLen(a_bit_count: usize) usize {...@@ -76,9 +76,18 @@ pub fn calcSqrtLimbsBufferLen(a_bit_count: usize) usize {
76 return a_limb_count + 3 * u_s_rem_limb_count + calcDivLimbsBufferLen(a_limb_count, u_s_rem_limb_count);76 return a_limb_count + 3 * u_s_rem_limb_count + calcDivLimbsBufferLen(a_limb_count, u_s_rem_limb_count);
77}77}
7878
79// Compute the number of limbs required to store a 2s-complement number of `bit_count` bits.79/// Compute the number of limbs required to store a 2s-complement number of `bit_count` bits.
80pub fn calcNonZeroTwosCompLimbCount(bit_count: usize) usize {
81 assert(bit_count != 0);
82 return calcTwosCompLimbCount(bit_count);
83}
84
85/// Compute the number of limbs required to store a 2s-complement number of `bit_count` bits.
86///
87/// Special cases `bit_count == 0` to return 1. Zero-bit integers can only store the value zero
88/// and this big integer implementation stores zero using one limb.
80pub fn calcTwosCompLimbCount(bit_count: usize) usize {89pub fn calcTwosCompLimbCount(bit_count: usize) usize {
81 return std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable;90 return @max(std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable, 1);
82}91}
8392
84/// a + b * c + *carry, sets carry to the overflow bits93/// a + b * c + *carry, sets carry to the overflow bits
...@@ -188,8 +197,10 @@ pub const Mutable = struct {...@@ -188,8 +197,10 @@ pub const Mutable = struct {
188 if (self.limbs.ptr != other.limbs.ptr) {197 if (self.limbs.ptr != other.limbs.ptr) {
189 @memcpy(self.limbs[0..other.limbs.len], other.limbs[0..other.limbs.len]);198 @memcpy(self.limbs[0..other.limbs.len], other.limbs[0..other.limbs.len]);
190 }199 }
191 self.positive = other.positive;200 // Normalize before setting `positive` so the `eqlZero` doesn't need to iterate
192 self.len = other.limbs.len;201 // over the extra zero limbs.
202 self.normalize(other.limbs.len);
203 self.positive = other.positive or other.eqlZero();
193 }204 }
194205
195 /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not206 /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not
lib/std/math/big/int_test.zig+28
...@@ -726,6 +726,34 @@ test "subWrap single-multi, signed, limb aligned" {...@@ -726,6 +726,34 @@ test "subWrap single-multi, signed, limb aligned" {
726 try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));726 try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
727}727}
728728
729test "addWrap returns normalized result" {
730 var x = try Managed.initSet(testing.allocator, 0);
731 defer x.deinit();
732 var y = try Managed.initSet(testing.allocator, 0);
733 defer y.deinit();
734
735 // make them both non normalized "-0"
736 x.setMetadata(false, 1);
737 y.setMetadata(false, 1);
738
739 var r = try Managed.init(testing.allocator);
740 defer r.deinit();
741 try testing.expect(!(try r.addWrap(&x, &y, .unsigned, 64)));
742 try testing.expect(r.isPositive() and r.len() == 1 and r.limbs[0] == 0);
743}
744
745test "subWrap returns normalized result" {
746 var x = try Managed.initSet(testing.allocator, 0);
747 defer x.deinit();
748 var y = try Managed.initSet(testing.allocator, 0);
749 defer y.deinit();
750
751 var r = try Managed.init(testing.allocator);
752 defer r.deinit();
753 try testing.expect(!(try r.subWrap(&x, &y, .unsigned, 64)));
754 try testing.expect(r.isPositive() and r.len() == 1 and r.limbs[0] == 0);
755}
756
729test "addSat single-single, unsigned" {757test "addSat single-single, unsigned" {
730 var a = try Managed.initSet(testing.allocator, maxInt(u17) - 5);758 var a = try Managed.initSet(testing.allocator, maxInt(u17) - 5);
731 defer a.deinit();759 defer a.deinit();
src/Value.zig+1-1
...@@ -2677,7 +2677,7 @@ pub fn shlSatScalar(...@@ -2677,7 +2677,7 @@ pub fn shlSatScalar(
2677 const shift: usize = @intCast(rhs.toUnsignedInt(zcu));2677 const shift: usize = @intCast(rhs.toUnsignedInt(zcu));
2678 const limbs = try arena.alloc(2678 const limbs = try arena.alloc(
2679 std.math.big.Limb,2679 std.math.big.Limb,
2680 std.math.big.int.calcTwosCompLimbCount(info.bits) + 1,2680 std.math.big.int.calcTwosCompLimbCount(info.bits),
2681 );2681 );
2682 var result_bigint = BigIntMutable{2682 var result_bigint = BigIntMutable{
2683 .limbs = limbs,2683 .limbs = limbs,
test/behavior/for.zig+9
...@@ -535,3 +535,12 @@ test "return from inline for" {...@@ -535,3 +535,12 @@ test "return from inline for" {
535 };535 };
536 try std.testing.expect(!S.do());536 try std.testing.expect(!S.do());
537}537}
538
539test "for loop 0 length range" {
540 const map: []const u8 = &.{};
541 for (map, 0..map.len) |i, j| {
542 _ = i;
543 _ = j;
544 comptime unreachable;
545 }
546}