1use crate::{
2 error::{assert_finite_operands, FpError, FpResult},
3 fbig::FBig,
4 helper_macros,
5 repr::{Context, Repr, Word},
6 round::{Round, Rounded},
7 utils::{digit_len, shl_digits, shl_digits_in_place, split_digits, split_digits_ref},
8};
9use core::{
10 cmp::Ordering,
11 ops::{Add, AddAssign, Sub, SubAssign},
12};
13
14use dashu_base::Sign::{self, *};
15use dashu_int::{IBig, UBig};
16
17fn cancel_zero<R: Round, const B: Word>(sig: IBig, exp: isize) -> Repr<B> {
20 if sig.is_zero() && R::IS_ROUND_TOWARD_NEGATIVE {
21 Repr::neg_zero()
22 } else {
23 Repr::new(sig, exp)
24 }
25}
26
27impl<R: Round, const B: Word> Add for FBig<R, B> {
28 type Output = Self;
29
30 #[inline]
31 fn add(self, rhs: Self) -> Self::Output {
32 add_val_val(self, rhs, Positive)
33 }
34}
35
36impl<R: Round, const B: Word> Add<&FBig<R, B>> for FBig<R, B> {
37 type Output = Self;
38
39 #[inline]
40 fn add(self, rhs: &FBig<R, B>) -> Self::Output {
41 add_val_ref(self, rhs, Positive)
42 }
43}
44
45impl<R: Round, const B: Word> Add<FBig<R, B>> for &FBig<R, B> {
46 type Output = FBig<R, B>;
47
48 #[inline]
49 fn add(self, rhs: FBig<R, B>) -> Self::Output {
50 add_ref_val(self, rhs, Positive)
51 }
52}
53
54impl<R: Round, const B: Word> Add<&FBig<R, B>> for &FBig<R, B> {
55 type Output = FBig<R, B>;
56
57 #[inline]
58 fn add(self, rhs: &FBig<R, B>) -> Self::Output {
59 add_ref_ref(self, rhs, Positive)
60 }
61}
62
63impl<R: Round, const B: Word> Sub for FBig<R, B> {
64 type Output = Self;
65
66 #[inline]
67 fn sub(self, rhs: Self) -> Self::Output {
68 add_val_val(self, rhs, Negative)
69 }
70}
71
72impl<R: Round, const B: Word> Sub<&FBig<R, B>> for FBig<R, B> {
73 type Output = Self;
74
75 #[inline]
76 fn sub(self, rhs: &FBig<R, B>) -> Self::Output {
77 add_val_ref(self, rhs, Negative)
78 }
79}
80
81impl<R: Round, const B: Word> Sub<FBig<R, B>> for &FBig<R, B> {
82 type Output = FBig<R, B>;
83
84 #[inline]
85 fn sub(self, rhs: FBig<R, B>) -> Self::Output {
86 add_ref_val(self, rhs, Negative)
87 }
88}
89
90impl<R: Round, const B: Word> Sub<&FBig<R, B>> for &FBig<R, B> {
91 type Output = FBig<R, B>;
92
93 #[inline]
94 fn sub(self, rhs: &FBig<R, B>) -> Self::Output {
95 add_ref_ref(self, rhs, Negative)
96 }
97}
98
99helper_macros::impl_binop_assign_by_taking!(impl AddAssign<Self>, add_assign, add);
100helper_macros::impl_binop_assign_by_taking!(impl SubAssign<Self>, sub_assign, sub);
101
102macro_rules! impl_add_sub_primitive_with_fbig {
103 ($($t:ty)*) => {$(
104 helper_macros::impl_binop_with_primitive!(impl Add<$t>, add);
105 helper_macros::impl_binop_assign_with_primitive!(impl AddAssign<$t>, add_assign);
106 helper_macros::impl_binop_with_primitive!(impl Sub<$t>, sub);
107 helper_macros::impl_binop_assign_with_primitive!(impl SubAssign<$t>, sub_assign);
108 )*};
109}
110impl_add_sub_primitive_with_fbig!(u8 u16 u32 u64 u128 usize UBig i8 i16 i32 i64 i128 isize IBig);
111
112fn add_val_val<R: Round, const B: Word>(
113 lhs: FBig<R, B>,
114 mut rhs: FBig<R, B>,
115 rhs_sign: Sign,
116) -> FBig<R, B> {
117 assert_finite_operands(&lhs.repr, &rhs.repr);
118
119 let context = Context::max(lhs.context, rhs.context);
120 rhs.repr.significand *= rhs_sign;
121 let sum = if lhs.repr.is_pos_zero() {
122 rhs.repr
123 } else if rhs.repr.is_pos_zero() {
124 lhs.repr
125 } else {
126 match lhs.repr.exponent.cmp(&rhs.repr.exponent) {
127 Ordering::Equal => context.repr_round(cancel_zero::<R, B>(
128 lhs.repr.significand + rhs.repr.significand,
129 lhs.repr.exponent,
130 )),
131 Ordering::Greater => context.repr_add_large_small(lhs.repr, &rhs.repr, Positive),
132 Ordering::Less => context.repr_add_small_large(lhs.repr, &rhs.repr, Positive),
133 }
134 .value()
135 };
136 FBig::new(sum, context)
137}
138
139fn add_val_ref<R: Round, const B: Word>(
140 lhs: FBig<R, B>,
141 rhs: &FBig<R, B>,
142 rhs_sign: Sign,
143) -> FBig<R, B> {
144 assert_finite_operands(&lhs.repr, &rhs.repr);
145
146 let context = Context::max(lhs.context, rhs.context);
147 let sum = if lhs.repr.is_pos_zero() {
148 let mut repr = rhs.repr.clone();
149 repr.significand *= rhs_sign;
150 repr
151 } else if rhs.repr.is_pos_zero() {
152 lhs.repr
153 } else {
154 match lhs.repr.exponent.cmp(&rhs.repr.exponent) {
155 Ordering::Equal => {
156 let sum_signif = match rhs_sign {
157 Positive => lhs.repr.significand + &rhs.repr.significand,
158 Negative => lhs.repr.significand - &rhs.repr.significand,
159 };
160 context.repr_round(cancel_zero::<R, B>(sum_signif, lhs.repr.exponent))
161 }
162 Ordering::Greater => context.repr_add_large_small(lhs.repr, &rhs.repr, rhs_sign),
163 Ordering::Less => context.repr_add_small_large(lhs.repr, &rhs.repr, rhs_sign),
164 }
165 .value()
166 };
167 FBig::new(sum, context)
168}
169
170fn add_ref_val<R: Round, const B: Word>(
171 lhs: &FBig<R, B>,
172 mut rhs: FBig<R, B>,
173 rhs_sign: Sign,
174) -> FBig<R, B> {
175 assert_finite_operands(&lhs.repr, &rhs.repr);
176
177 let context = Context::max(lhs.context, rhs.context);
178 rhs.repr.significand *= rhs_sign;
179 let sum = if lhs.repr.is_pos_zero() {
180 rhs.repr
181 } else if rhs.repr.is_pos_zero() {
182 lhs.repr.clone()
183 } else {
184 match lhs.repr.exponent.cmp(&rhs.repr.exponent) {
185 Ordering::Equal => context.repr_round(cancel_zero::<R, B>(
186 &lhs.repr.significand + rhs.repr.significand,
187 lhs.repr.exponent,
188 )),
189 Ordering::Greater => context.repr_add_small_large(rhs.repr, &lhs.repr, Positive),
190 Ordering::Less => context.repr_add_large_small(rhs.repr, &lhs.repr, Positive),
191 }
192 .value()
193 };
194 FBig::new(sum, context)
195}
196
197fn add_ref_ref<R: Round, const B: Word>(
198 lhs: &FBig<R, B>,
199 rhs: &FBig<R, B>,
200 rhs_sign: Sign,
201) -> FBig<R, B> {
202 assert_finite_operands(&lhs.repr, &rhs.repr);
203
204 let context = Context::max(lhs.context, rhs.context);
205 let sum = if lhs.repr.is_pos_zero() {
206 let mut repr = rhs.repr.clone();
207 repr.significand *= rhs_sign;
208 repr
209 } else if rhs.repr.is_pos_zero() {
210 lhs.repr.clone()
211 } else {
212 match lhs.repr.exponent.cmp(&rhs.repr.exponent) {
213 Ordering::Equal => context.repr_round(cancel_zero::<R, B>(
214 &lhs.repr.significand + rhs_sign * rhs.repr.significand.clone(),
215 lhs.repr.exponent,
216 )),
217 Ordering::Greater => {
218 context.repr_add_large_small(lhs.repr.clone(), &rhs.repr, rhs_sign)
219 }
220 Ordering::Less => context.repr_add_small_large(lhs.repr.clone(), &rhs.repr, rhs_sign),
221 }
222 .value()
223 };
224 FBig::new(sum, context)
225}
226
227impl<R: Round> Context<R> {
228 fn repr_round_sum<const B: Word>(
231 &self,
232 mut significand: IBig,
233 mut exponent: isize,
234 mut low: (IBig, usize),
235 is_sub: bool,
236 ) -> Rounded<Repr<B>> {
237 let neg_cancel = is_sub && R::IS_ROUND_TOWARD_NEGATIVE;
240 let make_repr = |sig: IBig, exp: isize| -> Repr<B> {
241 if sig.is_zero() && neg_cancel {
242 Repr::neg_zero()
243 } else {
244 Repr::new(sig, exp)
245 }
246 };
247
248 if !self.is_limited() {
249 return Rounded::Exact(make_repr(significand, exponent));
251 }
252
253 let rnd_precision = self.precision + is_sub as usize;
255
256 let digits = digit_len::<B>(&significand);
258 match digits.cmp(&rnd_precision) {
259 Ordering::Equal => {}
260 Ordering::Greater => {
261 let shift = digits - rnd_precision;
270 let (signif_hi, mut signif_lo) = split_digits::<B>(significand, shift);
271 significand = signif_hi;
272 exponent += shift as isize;
273 shl_digits_in_place::<B>(&mut signif_lo, low.1);
274 low.0 += signif_lo;
275 low.1 += shift;
276 }
277 Ordering::Less => {
278 if !low.0.is_zero() {
289 let (low_val, low_prec) = low;
290 let shift = low_prec.min(rnd_precision - digits);
291 let (pad, low_val) = split_digits::<B>(low_val, low_prec - shift);
292 shl_digits_in_place::<B>(&mut significand, shift);
293 exponent -= shift as isize;
294 significand += pad;
295 low = (low_val, low_prec - shift);
296 }
297 }
298 };
299
300 if low.0.is_zero() {
302 Rounded::Exact(make_repr(significand, exponent))
303 } else {
304 let adjust = R::round_fract::<B>(&significand, low.0, low.1);
307 Rounded::Inexact(make_repr(significand + adjust, exponent), adjust)
308 }
309 }
310
311 fn repr_add_large_small<const B: Word>(
313 &self,
314 mut lhs: Repr<B>,
315 rhs: &Repr<B>,
316 rhs_sign: Sign,
317 ) -> Rounded<Repr<B>> {
318 debug_assert!(lhs.exponent >= rhs.exponent);
319
320 let is_sub = lhs.significand.sign() != rhs_sign * rhs.significand.sign();
322 let rnd_precision = self.precision + is_sub as usize;
323
324 let ediff = (lhs.exponent - rhs.exponent) as usize;
325 let ldigits = lhs.digits();
326 let rdigits_est = rhs.digits_ub(); let low: (IBig, usize); let (significand, exponent) =
331 if self.is_limited() && is_sub && rdigits_est + self.precision >= ldigits + ediff {
332 shl_digits_in_place::<B>(&mut lhs.significand, ediff);
346 low = (IBig::ZERO, 0);
347 match rhs_sign {
348 Positive => (lhs.significand + &rhs.significand, rhs.exponent),
349 Negative => (lhs.significand - &rhs.significand, rhs.exponent),
350 }
351 } else if self.is_limited()
352 && rdigits_est + 1 < ediff
353 && rdigits_est + 1 + rnd_precision < ldigits + ediff
354 {
355 low = (rhs_sign * rhs.significand.signum(), ediff);
368 (lhs.significand, lhs.exponent)
369 } else if self.is_limited() && ldigits >= self.precision {
370 let (rhs_signif, r) = split_digits_ref::<B>(&rhs.significand, ediff);
382 low = (rhs_sign * r, ediff);
383 (lhs.significand + rhs_sign * rhs_signif, lhs.exponent)
384 } else if self.is_limited() && ediff + ldigits > self.precision {
385 let lshift = self.precision - ldigits;
399 let rshift = ediff - lshift;
400 let (rhs_signif, r) = split_digits_ref::<B>(&rhs.significand, rshift);
401 shl_digits_in_place::<B>(&mut lhs.significand, lshift);
402
403 low = (rhs_sign * r, rshift);
404 (lhs.significand + rhs_sign * rhs_signif, lhs.exponent - lshift as isize)
405 } else {
406 shl_digits_in_place::<B>(&mut lhs.significand, ediff);
418 low = (IBig::ZERO, 0);
419 match rhs_sign {
420 Positive => (lhs.significand + &rhs.significand, rhs.exponent),
421 Negative => (lhs.significand - &rhs.significand, rhs.exponent),
422 }
423 };
424
425 self.repr_round_sum(significand, exponent, low, is_sub)
426 }
427
428 fn repr_add_small_large<const B: Word>(
430 &self,
431 lhs: Repr<B>,
432 rhs: &Repr<B>,
433 rhs_sign: Sign,
434 ) -> Rounded<Repr<B>> {
435 debug_assert!(lhs.exponent <= rhs.exponent);
436
437 let is_sub = lhs.significand.sign() != rhs_sign * rhs.significand.sign();
440 let rnd_precision = self.precision + is_sub as usize;
441
442 let ediff = (rhs.exponent - lhs.exponent) as usize;
443 let rdigits = rhs.digits();
444 let ldigits_est = lhs.digits_ub();
445
446 let low: (IBig, usize);
448 let (significand, exponent) =
449 if self.is_limited() && is_sub && ldigits_est + self.precision >= rdigits + ediff {
450 let rhs_signif = shl_digits::<B>(&rhs.significand, ediff);
455 low = (IBig::ZERO, 0);
456 (rhs_sign * rhs_signif + lhs.significand, lhs.exponent)
457 } else if self.is_limited()
458 && ldigits_est + 1 < ediff
459 && ldigits_est + 1 + rnd_precision < rdigits + ediff
460 {
461 low = (lhs.significand.signum(), ediff);
467 (rhs_sign * rhs.significand.clone(), rhs.exponent)
468 } else if self.is_limited() && rdigits >= self.precision {
469 let (lhs_signif, r) = split_digits::<B>(lhs.significand, ediff);
471 low = (r, ediff);
472 match rhs_sign {
473 Positive => (lhs_signif + &rhs.significand, rhs.exponent),
474 Negative => (lhs_signif - &rhs.significand, rhs.exponent),
475 }
476 } else if self.is_limited() && ediff + rdigits > self.precision {
477 let lshift = self.precision - rdigits;
479 let rshift = ediff - lshift;
480 let (lhs_signif, r) = split_digits::<B>(lhs.significand, rshift);
481 let rhs_signif = shl_digits::<B>(&rhs.significand, lshift);
482
483 low = (r, rshift);
484 (rhs_sign * rhs_signif + lhs_signif, rhs.exponent - lshift as isize)
485 } else {
486 let rhs_signif = shl_digits::<B>(&rhs.significand, ediff);
488 low = (IBig::ZERO, 0);
489 (rhs_sign * rhs_signif + lhs.significand, lhs.exponent)
490 };
491
492 self.repr_round_sum(significand, exponent, low, is_sub)
493 }
494
495 pub fn add<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
513 if lhs.is_infinite() || rhs.is_infinite() {
514 return Err(FpError::InfiniteInput);
515 }
516
517 let sum = if lhs.is_pos_zero() {
518 self.repr_round_ref(rhs)
519 } else if rhs.is_pos_zero() {
520 self.repr_round_ref(lhs)
521 } else {
522 match lhs.exponent.cmp(&rhs.exponent) {
523 Ordering::Equal => {
524 let sig = &lhs.significand + &rhs.significand;
525 self.repr_round(cancel_zero::<R, B>(sig, lhs.exponent))
526 }
527 Ordering::Greater => self.repr_add_large_small(lhs.clone(), rhs, Positive),
528 Ordering::Less => self.repr_add_small_large(lhs.clone(), rhs, Positive),
529 }
530 };
531 Ok(sum.map(|v| FBig::new(v, *self)))
532 }
533
534 pub fn sub<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
555 if lhs.is_infinite() || rhs.is_infinite() {
556 return Err(FpError::InfiniteInput);
557 }
558
559 let sum = if lhs.is_pos_zero() {
560 self.repr_round_ref(&Repr::new(-&rhs.significand, rhs.exponent))
565 } else if rhs.is_pos_zero() {
566 self.repr_round_ref(lhs)
567 } else {
568 match lhs.exponent.cmp(&rhs.exponent) {
569 Ordering::Equal => {
570 let sig = &lhs.significand - &rhs.significand;
571 self.repr_round(cancel_zero::<R, B>(sig, lhs.exponent))
572 }
573 Ordering::Greater => self.repr_add_large_small(lhs.clone(), rhs, Negative),
574 Ordering::Less => self.repr_add_small_large(lhs.clone(), rhs, Negative),
575 }
576 };
577 Ok(sum.map(|v| FBig::new(v, *self)))
578 }
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584 use crate::round::mode::{HalfAway, HalfEven};
585
586 fn r<const B: Word>(sig: i128, exp: isize) -> Repr<B> {
588 Repr::new(IBig::from(sig), exp)
589 }
590
591 #[test]
596 fn sub_severe_cancellation_decimal() {
597 let ctx = Context::<HalfAway>::new(3);
598 assert_eq!(
600 ctx.sub(&r::<10>(100, -2), &r::<10>(99999999, -8))
601 .unwrap()
602 .value()
603 .repr(),
604 &r::<10>(1, -8)
605 );
606 assert_eq!(
608 ctx.sub(&r::<10>(100, -2), &r::<10>(99950001, -8))
609 .unwrap()
610 .value()
611 .repr(),
612 &r::<10>(500, -6)
613 );
614 }
615
616 #[test]
617 fn sub_severe_cancellation_binary() {
618 let ctx = Context::<HalfEven>::new(10);
619 assert_eq!(
621 ctx.sub(&r::<2>(1, 20), &r::<2>((1i128 << 20) - 1, 0))
622 .unwrap()
623 .value()
624 .repr(),
625 &r::<2>(1, 0)
626 );
627 assert_eq!(
629 ctx.sub(&r::<2>((1i128 << 20) - 1, 0), &r::<2>(1, 20))
630 .unwrap()
631 .value()
632 .repr(),
633 &r::<2>(-1, 0)
634 );
635 assert_eq!(
637 ctx.sub(&r::<2>(1, 30), &r::<2>((1i128 << 30) - 1, 0))
638 .unwrap()
639 .value()
640 .repr(),
641 &r::<2>(1, 0)
642 );
643 }
644
645 #[test]
648 fn add_effective_severe_cancellation() {
649 let ctx = Context::<HalfEven>::new(10);
650 assert_eq!(
652 ctx.add(&r::<2>(1, 20), &r::<2>(-((1i128 << 20) - 1), 0))
653 .unwrap()
654 .value()
655 .repr(),
656 &r::<2>(1, 0)
657 );
658 }
659
660 #[test]
662 fn sub_operator_severe_cancellation() {
663 let a = FBig::<HalfEven, 2>::from_parts(IBig::from(1), 20);
664 let b = FBig::<HalfEven, 2>::from_parts(IBig::from((1i128 << 20) - 1), 0);
665 assert_eq!((a - b).repr(), &r::<2>(1, 0));
666 }
667
668 #[test]
672 fn sub_mild_unchanged() {
673 let ctx = Context::<HalfAway>::new(3);
674 assert_eq!(
676 ctx.sub(&r::<10>(101, 0), &r::<10>(2, -1))
677 .unwrap()
678 .value()
679 .repr(),
680 &r::<10>(1008, -1)
681 );
682 }
683
684 #[test]
692 fn add_negligible_short_operand_no_spurious_ulp() {
693 let ctx = Context::<HalfAway>::new(10);
695 assert_eq!(
696 ctx.add(&r::<2>(1, 0), &r::<2>(1, -100))
697 .unwrap()
698 .value()
699 .repr(),
700 &r::<2>(1, 0)
701 );
702 assert_eq!(
703 ctx.sub(&r::<2>(1, 0), &r::<2>(1, -100))
704 .unwrap()
705 .value()
706 .repr(),
707 &r::<2>(1, 0)
708 );
709 let ctx = Context::<HalfAway>::new(50);
711 assert_eq!(
712 ctx.add(&r::<2>(0x12345, 0), &r::<2>(1, -200))
713 .unwrap()
714 .value()
715 .repr(),
716 &r::<2>(0x12345, 0)
717 );
718 let ctx = Context::<HalfAway>::new(10);
720 assert_eq!(
721 ctx.add(&r::<10>(1, 0), &r::<10>(1, -100))
722 .unwrap()
723 .value()
724 .repr(),
725 &r::<10>(1, 0)
726 );
727 }
728}