Skip to main content

oxinum_float/native/
float_add.rs

1//! Addition, subtraction, and negation for `BigFloat`.
2//!
3//! The strategy used here is exact-shift alignment + sign-aware mantissa
4//! arithmetic. Because [`BigUint`] is arbitrary-precision, we can always
5//! shift the higher-exponent mantissa *left* (no precision loss) so that
6//! both operands share the smaller exponent. After the integer add or sub,
7//! the result is rounded to the larger of the two operand precisions.
8
9use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
10
11use oxinum_core::Sign;
12use oxinum_int::native::BigUint;
13
14use super::float::{BigFloat, RoundingMode};
15use super::nonfinite::{nonfinite_propagate, BinOp};
16
17impl BigFloat {
18    /// Return `self + other`, rounding the result to
19    /// `max(self.precision, other.precision)` using banker's rounding.
20    pub fn add_ref(&self, other: &BigFloat) -> BigFloat {
21        self.add_ref_with_mode(other, RoundingMode::HalfEven)
22    }
23
24    /// Return `self + other`, rounding the result to
25    /// `max(self.precision, other.precision)` using the chosen rounding mode.
26    pub fn add_ref_with_mode(&self, other: &BigFloat, mode: RoundingMode) -> BigFloat {
27        // Non-finite fast-path: propagate NaN/Inf per IEEE 754.
28        if let Some(result) = nonfinite_propagate(self, other, BinOp::Add) {
29            return result;
30        }
31        let target_prec = self.precision.max(other.precision);
32        if self.is_zero() {
33            return other.clone().round_to_precision(target_prec, mode);
34        }
35        if other.is_zero() {
36            return self.clone().round_to_precision(target_prec, mode);
37        }
38
39        // Step 1: Align to the smaller exponent by shifting the higher-exp
40        // mantissa left by exp_diff.
41        let (common_exp, lhs_mag, rhs_mag) = align_to_common_exp(self, other);
42
43        // Step 2: Sign-aware add/sub of magnitudes.
44        let (out_sign, out_mag) = match (self.sign, other.sign) {
45            (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => {
46                (self.sign, &lhs_mag + &rhs_mag)
47            }
48            _ => {
49                // Magnitudes will be subtracted: we compute |lhs_mag - rhs_mag|
50                // and pick the sign from the larger operand. By construction
51                // lhs_mag corresponds to `self` and rhs_mag to `other`.
52                if lhs_mag >= rhs_mag {
53                    // |self| >= |other| -> sign of self.
54                    let diff = lhs_mag.checked_sub(&rhs_mag).unwrap_or_else(BigUint::zero);
55                    (self.sign, diff)
56                } else {
57                    let diff = rhs_mag.checked_sub(&lhs_mag).unwrap_or_else(BigUint::zero);
58                    (other.sign, diff)
59                }
60            }
61        };
62
63        // Step 3: Land back at the canonical form at target precision.
64        BigFloat::from_parts(out_sign, out_mag, common_exp, target_prec, mode)
65    }
66
67    /// Return `self - other` at `max(p_self, p_other)` precision, banker's
68    /// rounding.
69    pub fn sub_ref(&self, other: &BigFloat) -> BigFloat {
70        self.sub_ref_with_mode(other, RoundingMode::HalfEven)
71    }
72
73    /// Return `self - other` at `max(p_self, p_other)` precision with the
74    /// given rounding mode.
75    pub fn sub_ref_with_mode(&self, other: &BigFloat, mode: RoundingMode) -> BigFloat {
76        self.add_ref_with_mode(&other.neg(), mode)
77    }
78}
79
80/// Align two non-zero `BigFloat`s so they share the smaller of their two
81/// exponents. Returns `(common_exp, lhs_mantissa, rhs_mantissa)` such that
82/// `lhs.value = lhs_mantissa * 2^common_exp` and likewise for `rhs`.
83fn align_to_common_exp(a: &BigFloat, b: &BigFloat) -> (i64, BigUint, BigUint) {
84    if a.exponent == b.exponent {
85        (a.exponent, a.mantissa.clone(), b.mantissa.clone())
86    } else if a.exponent > b.exponent {
87        let shift = (a.exponent - b.exponent) as u64;
88        let lhs = a.mantissa.shl_bits(shift);
89        (b.exponent, lhs, b.mantissa.clone())
90    } else {
91        let shift = (b.exponent - a.exponent) as u64;
92        let rhs = b.mantissa.shl_bits(shift);
93        (a.exponent, a.mantissa.clone(), rhs)
94    }
95}
96
97// ---------------------------------------------------------------------------
98// Operator impls — owned and borrowed
99// ---------------------------------------------------------------------------
100
101impl Add<&BigFloat> for &BigFloat {
102    type Output = BigFloat;
103    #[inline]
104    fn add(self, rhs: &BigFloat) -> BigFloat {
105        self.add_ref(rhs)
106    }
107}
108
109impl Add<BigFloat> for BigFloat {
110    type Output = BigFloat;
111    #[inline]
112    fn add(self, rhs: BigFloat) -> BigFloat {
113        self.add_ref(&rhs)
114    }
115}
116
117impl Add<&BigFloat> for BigFloat {
118    type Output = BigFloat;
119    #[inline]
120    fn add(self, rhs: &BigFloat) -> BigFloat {
121        self.add_ref(rhs)
122    }
123}
124
125impl Add<BigFloat> for &BigFloat {
126    type Output = BigFloat;
127    #[inline]
128    fn add(self, rhs: BigFloat) -> BigFloat {
129        self.add_ref(&rhs)
130    }
131}
132
133impl Sub<&BigFloat> for &BigFloat {
134    type Output = BigFloat;
135    #[inline]
136    fn sub(self, rhs: &BigFloat) -> BigFloat {
137        self.sub_ref(rhs)
138    }
139}
140
141impl Sub<BigFloat> for BigFloat {
142    type Output = BigFloat;
143    #[inline]
144    fn sub(self, rhs: BigFloat) -> BigFloat {
145        self.sub_ref(&rhs)
146    }
147}
148
149impl Sub<&BigFloat> for BigFloat {
150    type Output = BigFloat;
151    #[inline]
152    fn sub(self, rhs: &BigFloat) -> BigFloat {
153        self.sub_ref(rhs)
154    }
155}
156
157impl Sub<BigFloat> for &BigFloat {
158    type Output = BigFloat;
159    #[inline]
160    fn sub(self, rhs: BigFloat) -> BigFloat {
161        self.sub_ref(&rhs)
162    }
163}
164
165impl AddAssign<&BigFloat> for BigFloat {
166    #[inline]
167    fn add_assign(&mut self, rhs: &BigFloat) {
168        *self = self.add_ref(rhs);
169    }
170}
171
172impl AddAssign<BigFloat> for BigFloat {
173    #[inline]
174    fn add_assign(&mut self, rhs: BigFloat) {
175        *self = self.add_ref(&rhs);
176    }
177}
178
179impl SubAssign<&BigFloat> for BigFloat {
180    #[inline]
181    fn sub_assign(&mut self, rhs: &BigFloat) {
182        *self = self.sub_ref(rhs);
183    }
184}
185
186impl SubAssign<BigFloat> for BigFloat {
187    #[inline]
188    fn sub_assign(&mut self, rhs: BigFloat) {
189        *self = self.sub_ref(&rhs);
190    }
191}
192
193impl Neg for BigFloat {
194    type Output = BigFloat;
195    #[inline]
196    fn neg(self) -> BigFloat {
197        BigFloat::neg(&self)
198    }
199}
200
201impl Neg for &BigFloat {
202    type Output = BigFloat;
203    #[inline]
204    fn neg(self) -> BigFloat {
205        BigFloat::neg(self)
206    }
207}