authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-01 14:05:55+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-04 11:25:29+02:00
logf1b3a90ef6b29f1b43b43350a05ffe75ddf7ca2c
treea707c9ed79f6c7c10a5bb67081c9bb487c540e6c
parentdc1f69854504c4dd7fbd18564c4a6811e3cc87e3

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.

2 files changed, 132 insertions(+), 8 deletions(-)

lib/std/math/big/int.zig+102-8
......@@ -86,6 +86,15 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
8686 return r1;
8787}
8888
89/// Used to indicate either limit of a 2s-complement integer.
90pub const TwosCompIntLimit = enum {
91 // The low limit, either 0x00 (unsigned) or (-)0x80 (signed) for an 8-bit integer.
92 min,
93
94 // The high limit, either 0xFF (unsigned) or 0x7F (signed) for an 8-bit integer.
95 max,
96};
97
8998/// A arbitrary-precision big integer, with a fixed set of mutable limbs.
9099pub const Mutable = struct {
91100 /// Raw digits. These are:
......@@ -287,6 +296,75 @@ pub const Mutable = struct {
287296 self.positive = positive;
288297 }
289298
299 /// Set self to either bound of a 2s-complement integer.
300 /// Note: The result is still sign-magnitude, not twos complement! In order to convert the
301 /// result to twos complement, it is sufficient to take the absolute value.
302 ///
303 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
304 /// r is `calcTwosCompLimbCount(bit_count)`.
305 pub fn setTwosCompIntLimit(
306 r: *Mutable,
307 limit: TwosCompIntLimit,
308 signedness: std.builtin.Signedness,
309 bit_count: usize
310 ) void {
311 // Handle zero-bit types.
312 if (bit_count == 0) {
313 r.set(0);
314 return;
315 }
316
317 const req_limbs = calcTwosCompLimbCount(bit_count);
318 const bit = @truncate(Log2Limb, bit_count - 1);
319 const signmask = @as(Limb, 1) << bit; // 0b0..010..0 where 1 is the sign bit.
320 const mask = (signmask << 1) -% 1; // 0b0..011..1 where the leftmost 1 is the sign bit.
321
322 r.positive = true;
323
324 switch (signedness) {
325 .signed => switch (limit) {
326 .min => {
327 // Negative bound, signed = -0x80.
328 r.len = req_limbs;
329 mem.set(Limb, r.limbs[0..r.len - 1], 0);
330 r.limbs[r.len - 1] = signmask;
331 r.positive = false;
332 },
333 .max => {
334 // Positive bound, signed = 0x7F
335 // Note, in this branch we need to normalize because the first bit is
336 // supposed to be 0.
337
338 // Special case for 1-bit integers.
339 if (bit_count == 1) {
340 r.set(0);
341 } else {
342 const new_req_limbs = calcTwosCompLimbCount(bit_count - 1);
343 const msb = @truncate(Log2Limb, bit_count - 2);
344 const new_signmask = @as(Limb, 1) << msb; // 0b0..010..0 where 1 is the sign bit.
345 const new_mask = (new_signmask << 1) -% 1; // 0b0..001..1 where the rightmost 0 is the sign bit.
346
347 r.len = new_req_limbs;
348 std.mem.set(Limb, r.limbs[0..r.len - 1], maxInt(Limb));
349 r.limbs[r.len - 1] = new_mask;
350 }
351 },
352 },
353 .unsigned => switch (limit) {
354 .min => {
355 // Min bound, unsigned = 0x00
356 r.set(0);
357 },
358 .max => {
359 // Max bound, unsigned = 0xFF
360 r.len = req_limbs;
361 std.mem.set(Limb, r.limbs[0..r.len - 1], maxInt(Limb));
362 r.limbs[r.len - 1] = mask;
363 },
364 },
365 }
366 }
367
290368 /// r = a + scalar
291369 ///
292370 /// r and a may be aliases.
......@@ -335,7 +413,6 @@ pub const Mutable = struct {
335413 }
336414
337415 /// r = a + b
338 ///
339416 /// r, a and b may be aliases.
340417 ///
341418 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
......@@ -353,8 +430,8 @@ pub const Mutable = struct {
353430 }
354431
355432 /// r = a + b with 2s-complement wrapping semantics.
356 ///
357433 /// r, a and b may be aliases
434 ///
358435 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
359436 /// r is `calcTwosCompLimbCount(bit_count)`.
360437 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 {
374451
375452 if (r.addCarry(x, y)) {
376453 // There are two possibilities here:
377 // - We overflowed req_limbs. In this case, the carry is ignored.
454 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
455 // truncate anyway.
378456 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
379457 const msl = math.max(a.limbs.len, b.limbs.len);
380458 if (msl < req_limbs) {
......@@ -399,7 +477,7 @@ pub const Mutable = struct {
399477 } else if (b.eqZero()) {
400478 r.copy(a);
401479 return false;
402 } if (a.positive != b.positive) {
480 } else if (a.positive != b.positive) {
403481 if (a.positive) {
404482 // (a) - (-b) => a + b
405483 return r.addCarry(a, b.abs());
......@@ -478,7 +556,8 @@ pub const Mutable = struct {
478556
479557 if (r.subCarry(x, y)) {
480558 // There are two possibilities here:
481 // - We overflowed req_limbs. In this case, the carry is ignored.
559 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
560 // truncate anyway.
482561 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
483562 const msl = math.max(a.limbs.len, b.limbs.len);
484563 if (msl < req_limbs) {
......@@ -1131,8 +1210,8 @@ pub const Mutable = struct {
11311210 }
11321211
11331212 const bit = @truncate(Log2Limb, bit_count - 1);
1134 const signmask = @as(Limb, 1) << bit;
1135 const mask = (signmask << 1) -% 1;
1213 const signmask = @as(Limb, 1) << bit; // 0b0..010...0 where 1 is the sign bit.
1214 const mask = (signmask << 1) -% 1; // 0b0..01..1 where the leftmost 1 is the sign bit.
11361215
11371216 if (!a.positive) {
11381217 // Convert the integer from sign-magnitude into twos-complement.
......@@ -1871,6 +1950,21 @@ pub const Managed = struct {
18711950 self.setMetadata(m.positive, m.len);
18721951 }
18731952
1953 /// Set self to either bound of a 2s-complement integer.
1954 /// Note: The result is still sign-magnitude, not twos complement! In order to convert the
1955 /// result to twos complement, it is sufficient to take the absolute value.
1956 pub fn setTwosCompIntLimit(
1957 r: *Managed,
1958 limit: TwosCompIntLimit,
1959 signedness: std.builtin.Signedness,
1960 bit_count: usize
1961 ) !void {
1962 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
1963 var m = r.toMutable();
1964 m.setTwosCompIntLimit(limit, signedness, bit_count);
1965 r.setMetadata(m.positive, m.len);
1966 }
1967
18741968 /// Converts self to a string in the requested base. Memory is allocated from the provided
18751969 /// allocator and not the one present in self.
18761970 pub fn toString(self: Managed, allocator: *Allocator, base: u8, case: std.fmt.Case) ![]u8 {
......@@ -2184,7 +2278,7 @@ pub const Managed = struct {
21842278 }
21852279 }
21862280
2187 // r = truncate(Int(signedness, bit_count), a)
2281 /// r = truncate(Int(signedness, bit_count), a)
21882282 pub fn truncate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void {
21892283 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
21902284 var m = r.toMutable();
lib/std/math/big/int_test.zig+30
......@@ -269,6 +269,36 @@ test "big.int string set bad base error" {
269269 try testing.expectError(error.InvalidBase, a.setString(45, "10"));
270270}
271271
272test "big.int twos complement limit set" {
273 const test_types = [_]type{
274 u64,
275 i64,
276 u1,
277 i1,
278 u0,
279 i0,
280 u65,
281 i65,
282 };
283
284 inline for (test_types) |T| {
285 // To work around 'control flow attempts to use compile-time variable at runtime'
286 const U = T;
287 const int_info = @typeInfo(U).Int;
288
289 var a = try Managed.init(testing.allocator);
290 defer a.deinit();
291
292 try a.setTwosCompIntLimit(.max, int_info.signedness, int_info.bits);
293 var max: U = maxInt(U);
294 try testing.expect(max == try a.to(U));
295
296 try a.setTwosCompIntLimit(.min, int_info.signedness, int_info.bits);
297 var min: U = minInt(U);
298 try testing.expect(min == try a.to(U));
299 }
300}
301
272302test "big.int string to" {
273303 var a = try Managed.initSet(testing.allocator, 120317241209124781241290847124);
274304 defer a.deinit();