diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 780930c3de..f6de62550e 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -76,9 +76,18 @@ pub fn calcSqrtLimbsBufferLen(a_bit_count: usize) usize { return a_limb_count + 3 * u_s_rem_limb_count + calcDivLimbsBufferLen(a_limb_count, u_s_rem_limb_count); } -// Compute the number of limbs required to store a 2s-complement number of `bit_count` bits. +/// Compute the number of limbs required to store a 2s-complement number of `bit_count` bits. +pub fn calcNonZeroTwosCompLimbCount(bit_count: usize) usize { + assert(bit_count != 0); + return calcTwosCompLimbCount(bit_count); +} + +/// Compute the number of limbs required to store a 2s-complement number of `bit_count` bits. +/// +/// Special cases `bit_count == 0` to return 1. Zero-bit integers can only store the value zero +/// and this big integer implementation stores zero using one limb. pub fn calcTwosCompLimbCount(bit_count: usize) usize { - return std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable; + return @max(std.math.divCeil(usize, bit_count, @bitSizeOf(Limb)) catch unreachable, 1); } /// a + b * c + *carry, sets carry to the overflow bits @@ -188,8 +197,10 @@ pub const Mutable = struct { if (self.limbs.ptr != other.limbs.ptr) { @memcpy(self.limbs[0..other.limbs.len], other.limbs[0..other.limbs.len]); } - self.positive = other.positive; - self.len = other.limbs.len; + // Normalize before setting `positive` so the `eqlZero` doesn't need to iterate + // over the extra zero limbs. + self.normalize(other.limbs.len); + self.positive = other.positive or other.eqlZero(); } /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 069aab5c81..489adc12fb 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -726,6 +726,34 @@ test "subWrap single-multi, signed, limb aligned" { try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); } +test "addWrap returns normalized result" { + var x = try Managed.initSet(testing.allocator, 0); + defer x.deinit(); + var y = try Managed.initSet(testing.allocator, 0); + defer y.deinit(); + + // make them both non normalized "-0" + x.setMetadata(false, 1); + y.setMetadata(false, 1); + + var r = try Managed.init(testing.allocator); + defer r.deinit(); + try testing.expect(!(try r.addWrap(&x, &y, .unsigned, 64))); + try testing.expect(r.isPositive() and r.len() == 1 and r.limbs[0] == 0); +} + +test "subWrap returns normalized result" { + var x = try Managed.initSet(testing.allocator, 0); + defer x.deinit(); + var y = try Managed.initSet(testing.allocator, 0); + defer y.deinit(); + + var r = try Managed.init(testing.allocator); + defer r.deinit(); + try testing.expect(!(try r.subWrap(&x, &y, .unsigned, 64))); + try testing.expect(r.isPositive() and r.len() == 1 and r.limbs[0] == 0); +} + test "addSat single-single, unsigned" { var a = try Managed.initSet(testing.allocator, maxInt(u17) - 5); defer a.deinit(); diff --git a/src/Value.zig b/src/Value.zig index 40e5331c4e..1e7dcc5fe2 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -2677,7 +2677,7 @@ pub fn shlSatScalar( const shift: usize = @intCast(rhs.toUnsignedInt(zcu)); const limbs = try arena.alloc( std.math.big.Limb, - std.math.big.int.calcTwosCompLimbCount(info.bits) + 1, + std.math.big.int.calcTwosCompLimbCount(info.bits), ); var result_bigint = BigIntMutable{ .limbs = limbs, diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 465a0f7d46..697497970f 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -535,3 +535,12 @@ test "return from inline for" { }; try std.testing.expect(!S.do()); } + +test "for loop 0 length range" { + const map: []const u8 = &.{}; + for (map, 0..map.len) |i, j| { + _ = i; + _ = j; + comptime unreachable; + } +}