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}