authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-01 14:27:52+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-04 11:25:29+02:00
log16991f920b4af8432ff97f83f02c3a4e614f480f
tree26b596590ffcc8f733aac47b73a9b437f8446424
parentf1b3a90ef6b29f1b43b43350a05ffe75ddf7ca2c

big ints: saturating addition


1 files changed, 51 insertions(+), 0 deletions(-)

lib/std/math/big/int.zig+51
...@@ -454,6 +454,7 @@ pub const Mutable = struct {...@@ -454,6 +454,7 @@ pub const Mutable = struct {
454 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by454 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
455 // truncate anyway.455 // truncate anyway.
456 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.456 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
457 // Note: after this we still might need to wrap.
457 const msl = math.max(a.limbs.len, b.limbs.len);458 const msl = math.max(a.limbs.len, b.limbs.len);
458 if (msl < req_limbs) {459 if (msl < req_limbs) {
459 r.limbs[msl] = 1;460 r.limbs[msl] = 1;
...@@ -464,6 +465,48 @@ pub const Mutable = struct {...@@ -464,6 +465,48 @@ pub const Mutable = struct {
464 r.truncate(r.toConst(), signedness, bit_count);465 r.truncate(r.toConst(), signedness, bit_count);
465 }466 }
466467
468 /// r = a + b with 2s-complement saturating semantics.
469 /// r, a and b may be aliases.
470 ///
471 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
472 /// r is `calcTwosCompLimbCount(bit_count)`.
473 pub fn addSat(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void {
474 const req_limbs = calcTwosCompLimbCount(bit_count);
475
476 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
477 // if an overflow occured.
478 const x = Const{
479 .positive = a.positive,
480 .limbs = a.limbs[0..math.min(req_limbs, a.limbs.len)],
481 };
482
483 const y = Const{
484 .positive = b.positive,
485 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
486 };
487
488 if (r.addCarry(x, y)) {
489 // There are two possibilities here:
490 // - We overflowed req_limbs, in which case we need to saturate.
491 // - a and b had less elements than req_limbs, and those were overflowed.
492 // Note: In this case, might _also_ need to saturate.
493 const msl = math.max(a.limbs.len, b.limbs.len);
494 if (msl < req_limbs) {
495 r.limbs[msl] = 1;
496 r.len = req_limbs;
497 // Note: Saturation may still be required if msl == req_limbs - 1
498 } else {
499 // Overflowed req_limbs, definitely saturate.
500 r.setTwosCompIntLimit(if (r.positive) .max else .min, signedness, bit_count);
501 }
502 }
503
504 // Saturate if the result didn't fit.
505 if (!r.toConst().fitsInTwosComp(signedness, bit_count)) {
506 r.setTwosCompIntLimit(if (r.positive) .max else .min, signedness, bit_count);
507 }
508 }
509
467 /// Base implementation for subtraction. Subtracts `max(a.limbs.len, b.limbs.len)` elements from a and b,510 /// Base implementation for subtraction. Subtracts `max(a.limbs.len, b.limbs.len)` elements from a and b,
468 /// and returns whether any overflow occured.511 /// and returns whether any overflow occured.
469 /// r, a and b may be aliases.512 /// r, a and b may be aliases.
...@@ -559,6 +602,7 @@ pub const Mutable = struct {...@@ -559,6 +602,7 @@ pub const Mutable = struct {
559 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by602 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
560 // truncate anyway.603 // truncate anyway.
561 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.604 // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled.
605 // Note: after this we still might need to wrap.
562 const msl = math.max(a.limbs.len, b.limbs.len);606 const msl = math.max(a.limbs.len, b.limbs.len);
563 if (msl < req_limbs) {607 if (msl < req_limbs) {
564 r.limbs[msl] = 1;608 r.limbs[msl] = 1;
...@@ -2070,6 +2114,13 @@ pub const Managed = struct {...@@ -2070,6 +2114,13 @@ pub const Managed = struct {
2070 r.setMetadata(m.positive, m.len);2114 r.setMetadata(m.positive, m.len);
2071 }2115 }
20722116
2117 pub fn addSat(r: *Managed, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) Allocator.Error!void {
2118 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2119 var m = r.toMutable();
2120 m.addSat(a, b, signedness, bit_count);
2121 r.setMetadata(m.positive, m.len);
2122 }
2123
2073 /// r = a - b2124 /// r = a - b
2074 ///2125 ///
2075 /// r, a and b may be aliases.2126 /// r, a and b may be aliases.