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};
29use oxinum_int::native::divrem;
30
31use super::float::{BigFloat, RoundingMode};
32use super::nonfinite::{nonfinite_binop, nonfinite_propagate, BinOp};
33
34/// Extra guard bits used during the quotient computation so the final round
35/// has well-defined round/sticky semantics. 8 bits ensures the round-bit and
36/// at least 7 sticky bits are present.
37const DIV_GUARD_BITS: u32 = 8;
38
39impl BigFloat {
40    /// Return `self / other` at `max(p_self, p_other)` precision using
41    /// banker's rounding.
42    ///
43    /// # Errors
44    ///
45    /// Returns [`OxiNumError::DivByZero`] if `other` is the canonical zero.
46    pub fn div_ref(&self, other: &BigFloat) -> OxiNumResult<BigFloat> {
47        self.div_ref_with_mode(other, RoundingMode::HalfEven)
48    }
49
50    /// Return `self / other` at `max(p_self, p_other)` precision with the
51    /// given rounding mode.
52    ///
53    /// # Errors
54    ///
55    /// Returns [`OxiNumError::DivByZero`] if `other` is the canonical zero.
56    pub fn div_ref_with_mode(
57        &self,
58        other: &BigFloat,
59        mode: RoundingMode,
60    ) -> OxiNumResult<BigFloat> {
61        // Non-finite fast-path: propagate NaN/Inf inputs per IEEE 754.
62        // Does NOT generate Inf from finite/0 — that stays as Err(DivByZero).
63        if let Some(result) = nonfinite_propagate(self, other, BinOp::Div) {
64            return Ok(result);
65        }
66        if other.is_zero() {
67            return Err(OxiNumError::DivByZero);
68        }
69        let target_prec = self.precision.max(other.precision);
70        if self.is_zero() {
71            // 0 / nonzero -> canonical zero at the target precision.
72            return Ok(BigFloat::zero(target_prec));
73        }
74        // Result sign: equal signs -> Positive, opposite signs -> Negative.
75        let out_sign = if self.sign == other.sign {
76            Sign::Positive
77        } else {
78            Sign::Negative
79        };
80        // Shift m_a left by `target_prec + guard` bits, then floor-divide
81        // by m_b. The quotient carries enough bits for from_parts to round
82        // back to `target_prec` correctly.
83        //
84        // CORRECTLY-ROUNDED DIVISION: we must also carry the division remainder
85        // into the sticky bit. Without this, if the low guard bits of the
86        // quotient happen to be zero but the remainder is nonzero, the
87        // round_drop_low_bits pass would compute sticky=false and round wrong
88        // (off by 1 ULP). Setting bit 0 of the quotient when remainder≠0
89        // is safe because DIV_GUARD_BITS >= 2, so bit 0 is strictly below the
90        // round bit and only affects the sticky bits.
91        let shift_bits = (target_prec + DIV_GUARD_BITS) as u64;
92        let scaled = self.mantissa.shl_bits(shift_bits);
93        let (mut quotient, remainder) = divrem(&scaled, &other.mantissa);
94        if !remainder.is_zero() {
95            // Ensure the sticky bit is set so that round_drop_low_bits can
96            // correctly round toward nearest-even when the true quotient is
97            // not an exact multiple of 2^(−shift_bits).
98            quotient.set_bit(0);
99        }
100        // Exponent of the bare integer quotient.
101        // value = quotient * 2^(e_a - e_b - shift_bits)
102        let exp_after_div = self
103            .exponent
104            .saturating_sub(other.exponent)
105            .saturating_sub(shift_bits as i64);
106        // Land at canonical form (normalize + round to target_prec).
107        Ok(BigFloat::from_parts(
108            out_sign,
109            quotient,
110            exp_after_div,
111            target_prec,
112            mode,
113        ))
114    }
115}
116
117// ---------------------------------------------------------------------------
118// Operator impls — owned and borrowed.
119//
120// The trait `Div` returns `Self::Output` (no Result). The operators follow
121// IEEE 754: finite/0 → ±Inf, 0/0 → NaN, Inf/Inf → NaN. Callers that need a
122// `Result` (and the DivByZero error on finite/0) should call `div_ref` /
123// `div_ref_with_mode` directly.
124// ---------------------------------------------------------------------------
125
126fn div_ieee(a: &BigFloat, b: &BigFloat) -> BigFloat {
127    // Operator path: use nonfinite_binop which generates Inf from finite/0.
128    if let Some(result) = nonfinite_binop(a, b, BinOp::Div) {
129        return result;
130    }
131    // Both finite, b is non-zero: call div_ref (normal path).
132    a.div_ref(b)
133        .expect("div_ieee: finite non-zero divisor guaranteed by nonfinite_binop guard")
134}
135
136impl Div<&BigFloat> for &BigFloat {
137    type Output = BigFloat;
138    #[inline]
139    fn div(self, rhs: &BigFloat) -> BigFloat {
140        div_ieee(self, rhs)
141    }
142}
143
144impl Div<BigFloat> for BigFloat {
145    type Output = BigFloat;
146    #[inline]
147    fn div(self, rhs: BigFloat) -> BigFloat {
148        div_ieee(&self, &rhs)
149    }
150}
151
152impl Div<&BigFloat> for BigFloat {
153    type Output = BigFloat;
154    #[inline]
155    fn div(self, rhs: &BigFloat) -> BigFloat {
156        div_ieee(&self, rhs)
157    }
158}
159
160impl Div<BigFloat> for &BigFloat {
161    type Output = BigFloat;
162    #[inline]
163    fn div(self, rhs: BigFloat) -> BigFloat {
164        div_ieee(self, &rhs)
165    }
166}
167
168impl DivAssign<&BigFloat> for BigFloat {
169    #[inline]
170    fn div_assign(&mut self, rhs: &BigFloat) {
171        *self = div_ieee(self, rhs);
172    }
173}
174
175impl DivAssign<BigFloat> for BigFloat {
176    #[inline]
177    fn div_assign(&mut self, rhs: BigFloat) {
178        *self = div_ieee(self, &rhs);
179    }
180}
181
182// ---------------------------------------------------------------------------
183// Rem (floating-point remainder: a - trunc(a/b)*b)
184//
185// This is the IEEE-style truncated remainder (not floor-based).  Follows
186// IEEE 754: Inf%y=NaN, x%0=NaN, finite%Inf=finite (returns lhs unchanged),
187// NaN propagates.
188// ---------------------------------------------------------------------------
189
190fn rem_core(a: &BigFloat, b: &BigFloat) -> BigFloat {
191    // IEEE 754 non-finite table for remainder.
192    if a.is_nan() || b.is_nan() || a.is_infinite() || b.is_zero() {
193        return BigFloat::nan(a.precision.max(b.precision));
194    }
195    // finite % Inf = the finite value (IEEE rule).
196    if b.is_infinite() {
197        return a.clone();
198    }
199    // Both finite, b nonzero.
200    if a.is_zero() {
201        return BigFloat::zero(a.precision.max(b.precision));
202    }
203    // quotient = a / b  (exact enough at high precision)
204    let prec = a.precision.max(b.precision);
205    let q = div_ieee(a, b);
206    // Truncate toward zero: drop the fractional part.
207    let q_trunc = q.trunc_to_integer(prec, RoundingMode::ToZero);
208    // remainder = a - trunc(a/b) * b
209    a.clone() - q_trunc * b.clone()
210}
211
212impl BigFloat {
213    /// Truncate mantissa to an integer value (discard bits below the binary
214    /// point). The result has the same precision as `self` rounded to `prec`.
215    fn trunc_to_integer(&self, prec: u32, mode: RoundingMode) -> Self {
216        if self.is_zero() {
217            return BigFloat::zero(prec);
218        }
219        // The value is  m * 2^exp  where m is the stored mantissa.
220        // If exp >= 0 the number is already an integer.
221        if self.exponent >= 0 {
222            return self.clone().with_precision(prec, mode);
223        }
224        // exponent < 0: the fractional part occupies |exp| bits of the mantissa.
225        let frac_bits = (-self.exponent) as u64;
226        let mant_bits = self.mantissa.bit_length();
227        if frac_bits >= mant_bits {
228            // Entirely fractional — truncates to zero.
229            return BigFloat::zero(prec);
230        }
231        // Drop the low `frac_bits` bits of the mantissa and set exponent = 0.
232        let int_mantissa = self.mantissa.shr_bits(frac_bits);
233        BigFloat::from_parts(self.sign, int_mantissa, 0, prec, mode)
234    }
235}
236
237impl Rem<&BigFloat> for &BigFloat {
238    type Output = BigFloat;
239    #[inline]
240    fn rem(self, rhs: &BigFloat) -> BigFloat {
241        rem_core(self, rhs)
242    }
243}
244
245impl Rem<BigFloat> for BigFloat {
246    type Output = BigFloat;
247    #[inline]
248    fn rem(self, rhs: BigFloat) -> BigFloat {
249        rem_core(&self, &rhs)
250    }
251}
252
253impl Rem<&BigFloat> for BigFloat {
254    type Output = BigFloat;
255    #[inline]
256    fn rem(self, rhs: &BigFloat) -> BigFloat {
257        rem_core(&self, rhs)
258    }
259}
260
261impl Rem<BigFloat> for &BigFloat {
262    type Output = BigFloat;
263    #[inline]
264    fn rem(self, rhs: BigFloat) -> BigFloat {
265        rem_core(self, &rhs)
266    }
267}
268
269impl RemAssign<&BigFloat> for BigFloat {
270    #[inline]
271    fn rem_assign(&mut self, rhs: &BigFloat) {
272        *self = rem_core(self, rhs);
273    }
274}
275
276impl RemAssign<BigFloat> for BigFloat {
277    #[inline]
278    fn rem_assign(&mut self, rhs: BigFloat) {
279        *self = rem_core(self, &rhs);
280    }
281}