| author | |
| committer | |
| log | 38ececf0a76867ff0e56f38879bb72344f9f9f17 |
| tree | 4526350f3c9ec46fca4ef98de2b1b3b6d5c6ab16 |
| parent | cb3eec285ff254cf36a1e48af06e764f8a9b23bd |
| signature |
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 | } |
| 78 | 78 | ||
| 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. |
| 80 | pub 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. | ||
| 80 | pub fn calcTwosCompLimbCount(bit_count: usize) usize { | 89 | pub 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 | } |
| 83 | 92 | ||
| 84 | /// a + b * c + *carry, sets carry to the overflow bits | 93 | /// 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 | } |
| 194 | 205 | ||
| 195 | /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not | 206 | /// 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 | } |
| 728 | 728 | ||
| 729 | test "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 | |||
| 745 | test "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 | |||
| 729 | test "addSat single-single, unsigned" { | 757 | test "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 | |||
| 539 | test "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 | } |