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}