mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 22:04:21 +00:00
big ints: setTwosCompIntLimit
This function can be used to initialize a big integer to either the upper or lower limit of a 2s-complement integer. Note that the result is still in sign-magnitude representation, though in order to convert it into twos complement all one has to do is take the absolute value.
This commit is contained in:
parent
dc1f698545
commit
f1b3a90ef6
2 changed files with 132 additions and 8 deletions
|
|
@ -86,6 +86,15 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
|
||||||
return r1;
|
return r1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to indicate either limit of a 2s-complement integer.
|
||||||
|
pub const TwosCompIntLimit = enum {
|
||||||
|
// The low limit, either 0x00 (unsigned) or (-)0x80 (signed) for an 8-bit integer.
|
||||||
|
min,
|
||||||
|
|
||||||
|
// The high limit, either 0xFF (unsigned) or 0x7F (signed) for an 8-bit integer.
|
||||||
|
max,
|
||||||
|
};
|
||||||
|
|
||||||
/// A arbitrary-precision big integer, with a fixed set of mutable limbs.
|
/// A arbitrary-precision big integer, with a fixed set of mutable limbs.
|
||||||
pub const Mutable = struct {
|
pub const Mutable = struct {
|
||||||
/// Raw digits. These are:
|
/// Raw digits. These are:
|
||||||
|
|
@ -287,6 +296,75 @@ pub const Mutable = struct {
|
||||||
self.positive = positive;
|
self.positive = positive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set self to either bound of a 2s-complement integer.
|
||||||
|
/// Note: The result is still sign-magnitude, not twos complement! In order to convert the
|
||||||
|
/// result to twos complement, it is sufficient to take the absolute value.
|
||||||
|
///
|
||||||
|
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
|
||||||
|
/// r is `calcTwosCompLimbCount(bit_count)`.
|
||||||
|
pub fn setTwosCompIntLimit(
|
||||||
|
r: *Mutable,
|
||||||
|
limit: TwosCompIntLimit,
|
||||||
|
signedness: std.builtin.Signedness,
|
||||||
|
bit_count: usize
|
||||||
|
) void {
|
||||||
|
// Handle zero-bit types.
|
||||||
|
if (bit_count == 0) {
|
||||||
|
r.set(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const req_limbs = calcTwosCompLimbCount(bit_count);
|
||||||
|
const bit = @truncate(Log2Limb, bit_count - 1);
|
||||||
|
const signmask = @as(Limb, 1) << bit; // 0b0..010..0 where 1 is the sign bit.
|
||||||
|
const mask = (signmask << 1) -% 1; // 0b0..011..1 where the leftmost 1 is the sign bit.
|
||||||
|
|
||||||
|
r.positive = true;
|
||||||
|
|
||||||
|
switch (signedness) {
|
||||||
|
.signed => switch (limit) {
|
||||||
|
.min => {
|
||||||
|
// Negative bound, signed = -0x80.
|
||||||
|
r.len = req_limbs;
|
||||||
|
mem.set(Limb, r.limbs[0..r.len - 1], 0);
|
||||||
|
r.limbs[r.len - 1] = signmask;
|
||||||
|
r.positive = false;
|
||||||
|
},
|
||||||
|
.max => {
|
||||||
|
// Positive bound, signed = 0x7F
|
||||||
|
// Note, in this branch we need to normalize because the first bit is
|
||||||
|
// supposed to be 0.
|
||||||
|
|
||||||
|
// Special case for 1-bit integers.
|
||||||
|
if (bit_count == 1) {
|
||||||
|
r.set(0);
|
||||||
|
} else {
|
||||||
|
const new_req_limbs = calcTwosCompLimbCount(bit_count - 1);
|
||||||
|
const msb = @truncate(Log2Limb, bit_count - 2);
|
||||||
|
const new_signmask = @as(Limb, 1) << msb; // 0b0..010..0 where 1 is the sign bit.
|
||||||
|
const new_mask = (new_signmask << 1) -% 1; // 0b0..001..1 where the rightmost 0 is the sign bit.
|
||||||
|
|
||||||
|
r.len = new_req_limbs;
|
||||||
|
std.mem.set(Limb, r.limbs[0..r.len - 1], maxInt(Limb));
|
||||||
|
r.limbs[r.len - 1] = new_mask;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.unsigned => switch (limit) {
|
||||||
|
.min => {
|
||||||
|
// Min bound, unsigned = 0x00
|
||||||
|
r.set(0);
|
||||||
|
},
|
||||||
|
.max => {
|
||||||
|
// Max bound, unsigned = 0xFF
|
||||||
|
r.len = req_limbs;
|
||||||
|
std.mem.set(Limb, r.limbs[0..r.len - 1], maxInt(Limb));
|
||||||
|
r.limbs[r.len - 1] = mask;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// r = a + scalar
|
/// r = a + scalar
|
||||||
///
|
///
|
||||||
/// r and a may be aliases.
|
/// r and a may be aliases.
|
||||||
|
|
@ -335,7 +413,6 @@ pub const Mutable = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// r = a + b
|
/// r = a + b
|
||||||
///
|
|
||||||
/// r, a and b may be aliases.
|
/// r, a and b may be aliases.
|
||||||
///
|
///
|
||||||
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
|
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
|
||||||
|
|
@ -353,8 +430,8 @@ pub const Mutable = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// r = a + b with 2s-complement wrapping semantics.
|
/// r = a + b with 2s-complement wrapping semantics.
|
||||||
///
|
|
||||||
/// r, a and b may be aliases
|
/// r, a and b may be aliases
|
||||||
|
///
|
||||||
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
|
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
|
||||||
/// r is `calcTwosCompLimbCount(bit_count)`.
|
/// r is `calcTwosCompLimbCount(bit_count)`.
|
||||||
pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
|
pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
|
||||||
|
|
@ -374,7 +451,8 @@ pub const Mutable = struct {
|
||||||
|
|
||||||
if (r.addCarry(x, y)) {
|
if (r.addCarry(x, y)) {
|
||||||
// There are two possibilities here:
|
// There are two possibilities here:
|
||||||
// - We overflowed req_limbs. In this case, the carry is ignored.
|
// - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
|
||||||
|
// truncate anyway.
|
||||||
// - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
|
// - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
|
||||||
const msl = math.max(a.limbs.len, b.limbs.len);
|
const msl = math.max(a.limbs.len, b.limbs.len);
|
||||||
if (msl < req_limbs) {
|
if (msl < req_limbs) {
|
||||||
|
|
@ -399,7 +477,7 @@ pub const Mutable = struct {
|
||||||
} else if (b.eqZero()) {
|
} else if (b.eqZero()) {
|
||||||
r.copy(a);
|
r.copy(a);
|
||||||
return false;
|
return false;
|
||||||
} if (a.positive != b.positive) {
|
} else if (a.positive != b.positive) {
|
||||||
if (a.positive) {
|
if (a.positive) {
|
||||||
// (a) - (-b) => a + b
|
// (a) - (-b) => a + b
|
||||||
return r.addCarry(a, b.abs());
|
return r.addCarry(a, b.abs());
|
||||||
|
|
@ -478,7 +556,8 @@ pub const Mutable = struct {
|
||||||
|
|
||||||
if (r.subCarry(x, y)) {
|
if (r.subCarry(x, y)) {
|
||||||
// There are two possibilities here:
|
// There are two possibilities here:
|
||||||
// - We overflowed req_limbs. In this case, the carry is ignored.
|
// - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
|
||||||
|
// truncate anyway.
|
||||||
// - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
|
// - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
|
||||||
const msl = math.max(a.limbs.len, b.limbs.len);
|
const msl = math.max(a.limbs.len, b.limbs.len);
|
||||||
if (msl < req_limbs) {
|
if (msl < req_limbs) {
|
||||||
|
|
@ -1131,8 +1210,8 @@ pub const Mutable = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const bit = @truncate(Log2Limb, bit_count - 1);
|
const bit = @truncate(Log2Limb, bit_count - 1);
|
||||||
const signmask = @as(Limb, 1) << bit;
|
const signmask = @as(Limb, 1) << bit; // 0b0..010...0 where 1 is the sign bit.
|
||||||
const mask = (signmask << 1) -% 1;
|
const mask = (signmask << 1) -% 1; // 0b0..01..1 where the leftmost 1 is the sign bit.
|
||||||
|
|
||||||
if (!a.positive) {
|
if (!a.positive) {
|
||||||
// Convert the integer from sign-magnitude into twos-complement.
|
// Convert the integer from sign-magnitude into twos-complement.
|
||||||
|
|
@ -1871,6 +1950,21 @@ pub const Managed = struct {
|
||||||
self.setMetadata(m.positive, m.len);
|
self.setMetadata(m.positive, m.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set self to either bound of a 2s-complement integer.
|
||||||
|
/// Note: The result is still sign-magnitude, not twos complement! In order to convert the
|
||||||
|
/// result to twos complement, it is sufficient to take the absolute value.
|
||||||
|
pub fn setTwosCompIntLimit(
|
||||||
|
r: *Managed,
|
||||||
|
limit: TwosCompIntLimit,
|
||||||
|
signedness: std.builtin.Signedness,
|
||||||
|
bit_count: usize
|
||||||
|
) !void {
|
||||||
|
try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
|
||||||
|
var m = r.toMutable();
|
||||||
|
m.setTwosCompIntLimit(limit, signedness, bit_count);
|
||||||
|
r.setMetadata(m.positive, m.len);
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts self to a string in the requested base. Memory is allocated from the provided
|
/// Converts self to a string in the requested base. Memory is allocated from the provided
|
||||||
/// allocator and not the one present in self.
|
/// allocator and not the one present in self.
|
||||||
pub fn toString(self: Managed, allocator: *Allocator, base: u8, case: std.fmt.Case) ![]u8 {
|
pub fn toString(self: Managed, allocator: *Allocator, base: u8, case: std.fmt.Case) ![]u8 {
|
||||||
|
|
@ -2184,7 +2278,7 @@ pub const Managed = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// r = truncate(Int(signedness, bit_count), a)
|
/// r = truncate(Int(signedness, bit_count), a)
|
||||||
pub fn truncate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {
|
pub fn truncate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {
|
||||||
try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
|
try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
|
||||||
var m = r.toMutable();
|
var m = r.toMutable();
|
||||||
|
|
|
||||||
|
|
@ -269,6 +269,36 @@ test "big.int string set bad base error" {
|
||||||
try testing.expectError(error.InvalidBase, a.setString(45, "10"));
|
try testing.expectError(error.InvalidBase, a.setString(45, "10"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "big.int twos complement limit set" {
|
||||||
|
const test_types = [_]type{
|
||||||
|
u64,
|
||||||
|
i64,
|
||||||
|
u1,
|
||||||
|
i1,
|
||||||
|
u0,
|
||||||
|
i0,
|
||||||
|
u65,
|
||||||
|
i65,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline for (test_types) |T| {
|
||||||
|
// To work around 'control flow attempts to use compile-time variable at runtime'
|
||||||
|
const U = T;
|
||||||
|
const int_info = @typeInfo(U).Int;
|
||||||
|
|
||||||
|
var a = try Managed.init(testing.allocator);
|
||||||
|
defer a.deinit();
|
||||||
|
|
||||||
|
try a.setTwosCompIntLimit(.max, int_info.signedness, int_info.bits);
|
||||||
|
var max: U = maxInt(U);
|
||||||
|
try testing.expect(max == try a.to(U));
|
||||||
|
|
||||||
|
try a.setTwosCompIntLimit(.min, int_info.signedness, int_info.bits);
|
||||||
|
var min: U = minInt(U);
|
||||||
|
try testing.expect(min == try a.to(U));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "big.int string to" {
|
test "big.int string to" {
|
||||||
var a = try Managed.initSet(testing.allocator, 120317241209124781241290847124);
|
var a = try Managed.initSet(testing.allocator, 120317241209124781241290847124);
|
||||||
defer a.deinit();
|
defer a.deinit();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue