Merge pull request #23310 from Rexicon226/fix-23309

big.int: return normalized results from `{add,sub}Carry`
This commit is contained in:
Alex Rønne Petersen 2025-03-25 18:44:35 +01:00
parent cb3eec285f
commit 38ececf0a7
No known key found for this signature in database
4 changed files with 53 additions and 5 deletions

View file

@ -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); 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 { 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 /// 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) { if (self.limbs.ptr != other.limbs.ptr) {
@memcpy(self.limbs[0..other.limbs.len], other.limbs[0..other.limbs.len]); @memcpy(self.limbs[0..other.limbs.len], other.limbs[0..other.limbs.len]);
} }
self.positive = other.positive; // Normalize before setting `positive` so the `eqlZero` doesn't need to iterate
self.len = other.limbs.len; // 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 /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not

View file

@ -726,6 +726,34 @@ test "subWrap single-multi, signed, limb aligned" {
try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); 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" { test "addSat single-single, unsigned" {
var a = try Managed.initSet(testing.allocator, maxInt(u17) - 5); var a = try Managed.initSet(testing.allocator, maxInt(u17) - 5);
defer a.deinit(); defer a.deinit();

View file

@ -2677,7 +2677,7 @@ pub fn shlSatScalar(
const shift: usize = @intCast(rhs.toUnsignedInt(zcu)); const shift: usize = @intCast(rhs.toUnsignedInt(zcu));
const limbs = try arena.alloc( const limbs = try arena.alloc(
std.math.big.Limb, std.math.big.Limb,
std.math.big.int.calcTwosCompLimbCount(info.bits) + 1, std.math.big.int.calcTwosCompLimbCount(info.bits),
); );
var result_bigint = BigIntMutable{ var result_bigint = BigIntMutable{
.limbs = limbs, .limbs = limbs,

View file

@ -535,3 +535,12 @@ test "return from inline for" {
}; };
try std.testing.expect(!S.do()); 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;
}
}