Skip to main content

oxinum_float/native/
float_div.rs

1//! Division for `BigFloat`.
2//!
3//! Strategy: rigorous integer division on scaled mantissas. Conceptually,
4//!
5//! ```text
6//! a / b = (m_a * 2^e_a) / (m_b * 2^e_b)
7//!       = (m_a / m_b) * 2^(e_a - e_b)
8//! ```
9//!
10//! `m_a / m_b` is generally not an integer, so to get `target_prec` correct
11//! bits in the result mantissa we left-shift `m_a` by `shift = target_prec +
12//! guard` bits before integer-dividing — that promotes `guard` extra bits of
13//! quotient resolution which the post-division rounding pass then consumes.
14//!
15//! Result exponent is `e_a - e_b - shift`. We use saturating arithmetic to
16//! mirror the convention established in `float_add.rs` /
17//! `round_to_precision_in_place`.
18//!
19//! This is the "simple reference division" path called out in the N4b plan
20//! as the rigorous fallback for the Newton-Raphson reciprocal approach.
21//! Trade-off: one big integer multiply (the shift) plus one big integer
22//! division. For the precisions OxiNum targets (a few hundred to a few
23//! thousand bits) this is comfortably fast, and crucially it is exact — no
24//! seed-convergence questions to debug at extreme exponents.
25
26use core::ops::{Div, DivAssign, Rem, RemAssign};
27
28use oxinum_core::{OxiNumError, OxiNumResult, Sign};
29
30use super::float::{BigFloat, RoundingMode};
31use super::nonfinite::{nonfinite_binop, nonfinite_propagate, BinOp};
32
33/// Extra guard bits used during the quotient computation so the final round
34/// has well-defined round/sticky semantics. 8 bits ensures the round-bit and
35/// at least 7 sticky bits are present.
36const DIV_GUARD_BITS: u32 = 8;
37
38impl BigFloat {
39    /// Return `self / other` at `max(p_self, p_other)` precision using
40    /// banker's rounding.
41    ///
42    /// # Errors
43    ///
44    /// Returns [`OxiNumError::DivByZero`] if `other` is the canonical zero.
45    pub fn div_ref(&self, other: &BigFloat) -> OxiNumResult<BigFloat> {
46        self.div_ref_with_mode(other, RoundingMode::HalfEven)
47    }
48
49    /// Return `self / other` at `max(p_self, p_other)` precision with the
50    /// given rounding mode.
51    ///
52    /// # Errors
53    ///
54    /// Returns [`OxiNumError::DivByZero`] if `other` is the canonical zero.
55    pub fn div_ref_with_mode(
56        &self,
57        other: &BigFloat,
58        mode: RoundingMode,
59    ) -> OxiNumResult<BigFloat> {
60        // Non-finite fast-path: propagate NaN/Inf inputs per IEEE 754.
61        // Does NOT generate Inf from finite/0 — that stays as Err(DivByZero).
62        if let Some(result) = nonfinite_propagate(self, other, BinOp::Div) {
63            return Ok(result);
64        }
65        if other.is_zero() {
66            return Err(OxiNumError::DivByZero);
67        }
68        let target_prec = self.precision.max(other.precision);
69        if self.is_zero() {
70            // 0 / nonzero -> canonical zero at the target precision.
71            return Ok(BigFloat::zero(target_prec));
72        }
73        // Result sign: equal signs -> Positive, opposite signs -> Negative.
74        let out_sign = if self.sign == other.sign {
75            Sign::Positive
76        } else {
77            Sign::Negative
78        };
79        // Shift m_a left by `target_prec + guard` bits, then floor-divide
80        // by m_b. The quotient carries enough bits for from_parts to round
81        // back to `target_prec` correctly.
82        let shift_bits = (target_prec + DIV_GUARD_BITS) as u64;
83        let scaled = self.mantissa.shl_bits(shift_bits);
84        let quotient = &scaled / &other.mantissa;
85        // Exponent of the bare integer quotient.
86        // value = quotient * 2^(e_a - e_b - shift_bits)
87        let exp_after_div = self
88            .exponent
89            .saturating_sub(other.exponent)
90            .saturating_sub(shift_bits as i64);
91        // Land at canonical form (normalize + round to target_prec).
92        Ok(BigFloat::from_parts(
93            out_sign,
94            quotient,
95            exp_after_div,
96            target_prec,
97            mode,
98        ))
99    }
100}
101
102// ---------------------------------------------------------------------------
103// Operator impls — owned and borrowed.
104//
105// The trait `Div` returns `Self::Output` (no Result). The operators follow
106// IEEE 754: finite/0 → ±Inf, 0/0 → NaN, Inf/Inf → NaN. Callers that need a
107// `Result` (and the DivByZero error on finite/0) should call `div_ref` /
108// `div_ref_with_mode` directly.
109// ---------------------------------------------------------------------------
110
111fn div_ieee(a: &BigFloat, b: &BigFloat) -> BigFloat {
112    // Operator path: use nonfinite_binop which generates Inf from finite/0.
113    if let Some(result) = nonfinite_binop(a, b, BinOp::Div) {
114        return result;
115    }
116    // Both finite, b is non-zero: call div_ref (normal path).
117    a.div_ref(b)
118        .expect("div_ieee: finite non-zero divisor guaranteed by nonfinite_binop guard")
119}
120
121impl Div<&BigFloat> for &BigFloat {
122    type Output = BigFloat;
123    #[inline]
124    fn div(self, rhs: &BigFloat) -> BigFloat {
125        div_ieee(self, rhs)
126    }
127}
128
129impl Div<BigFloat> for BigFloat {
130    type Output = BigFloat;
131    #[inline]
132    fn div(self, rhs: BigFloat) -> BigFloat {
133        div_ieee(&self, &rhs)
134    }
135}
136
137impl Div<&BigFloat> for BigFloat {
138    type Output = BigFloat;
139    #[inline]
140    fn div(self, rhs: &BigFloat) -> BigFloat {
141        div_ieee(&self, rhs)
142    }
143}
144
145impl Div<BigFloat> for &BigFloat {
146    type Output = BigFloat;
147    #[inline]
148    fn div(self, rhs: BigFloat) -> BigFloat {
149        div_ieee(self, &rhs)
150    }
151}
152
153impl DivAssign<&BigFloat> for BigFloat {
154    #[inline]
155    fn div_assign(&mut self, rhs: &BigFloat) {
156        *self = div_ieee(self, rhs);
157    }
158}
159
160impl DivAssign<BigFloat> for BigFloat {
161    #[inline]
162    fn div_assign(&mut self, rhs: BigFloat) {
163        *self = div_ieee(self, &rhs);
164    }
165}
166
167// ---------------------------------------------------------------------------
168// Rem (floating-point remainder: a - trunc(a/b)*b)
169//
170// This is the IEEE-style truncated remainder (not floor-based).  Follows
171// IEEE 754: Inf%y=NaN, x%0=NaN, finite%Inf=finite (returns lhs unchanged),
172// NaN propagates.
173// ---------------------------------------------------------------------------
174
175fn rem_core(a: &BigFloat, b: &BigFloat) -> BigFloat {
176    // IEEE 754 non-finite table for remainder.
177    if a.is_nan() || b.is_nan() || a.is_infinite() || b.is_zero() {
178        return BigFloat::nan(a.precision.max(b.precision));
179    }
180    // finite % Inf = the finite value (IEEE rule).
181    if b.is_infinite() {
182        return a.clone();
183    }
184    // Both finite, b nonzero.
185    if a.is_zero() {
186        return BigFloat::zero(a.precision.max(b.precision));
187    }
188    // quotient = a / b  (exact enough at high precision)
189    let prec = a.precision.max(b.precision);
190    let q = div_ieee(a, b);
191    // Truncate toward zero: drop the fractional part.
192    let q_trunc = q.trunc_to_integer(prec, RoundingMode::ToZero);
193    // remainder = a - trunc(a/b) * b
194    a.clone() - q_trunc * b.clone()
195}
196
197impl BigFloat {
198    /// Truncate mantissa to an integer value (discard bits below the binary
199    /// point). The result has the same precision as `self` rounded to `prec`.
200    fn trunc_to_integer(&self, prec: u32, mode: RoundingMode) -> Self {
201        if self.is_zero() {
202            return BigFloat::zero(prec);
203        }
204        // The value is  m * 2^exp  where m is the stored mantissa.
205        // If exp >= 0 the number is already an integer.
206        if self.exponent >= 0 {
207            return self.clone().with_precision(prec, mode);
208        }
209        // exponent < 0: the fractional part occupies |exp| bits of the mantissa.
210        let frac_bits = (-self.exponent) as u64;
211        let mant_bits = self.mantissa.bit_length();
212        if frac_bits >= mant_bits {
213            // Entirely fractional — truncates to zero.
214            return BigFloat::zero(prec);
215        }
216        // Drop the low `frac_bits` bits of the mantissa and set exponent = 0.
217        let int_mantissa = self.mantissa.shr_bits(frac_bits);
218        BigFloat::from_parts(self.sign, int_mantissa, 0, prec, mode)
219    }
220}
221
222impl Rem<&BigFloat> for &BigFloat {
223    type Output = BigFloat;
224    #[inline]
225    fn rem(self, rhs: &BigFloat) -> BigFloat {
226        rem_core(self, rhs)
227    }
228}
229
230impl Rem<BigFloat> for BigFloat {
231    type Output = BigFloat;
232    #[inline]
233    fn rem(self, rhs: BigFloat) -> BigFloat {
234        rem_core(&self, &rhs)
235    }
236}
237
238impl Rem<&BigFloat> for BigFloat {
239    type Output = BigFloat;
240    #[inline]
241    fn rem(self, rhs: &BigFloat) -> BigFloat {
242        rem_core(&self, rhs)
243    }
244}
245
246impl Rem<BigFloat> for &BigFloat {
247    type Output = BigFloat;
248    #[inline]
249    fn rem(self, rhs: BigFloat) -> BigFloat {
250        rem_core(self, &rhs)
251    }
252}
253
254impl RemAssign<&BigFloat> for BigFloat {
255    #[inline]
256    fn rem_assign(&mut self, rhs: &BigFloat) {
257        *self = rem_core(self, rhs);
258    }
259}
260
261impl RemAssign<BigFloat> for BigFloat {
262    #[inline]
263    fn rem_assign(&mut self, rhs: BigFloat) {
264        *self = rem_core(self, &rhs);
265    }
266}