1use crate::{
2 error::{assert_finite_operands, assert_limited_precision, FpError, FpResult},
3 fbig::FBig,
4 helper_macros::{self, impl_binop_assign_by_taking},
5 repr::{Context, Repr, Word},
6 round::{Round, Rounded, Rounding},
7 utils::{digit_len, shl_digits_in_place, split_digits},
8};
9use core::ops::{Div, DivAssign, Rem, RemAssign};
10use dashu_base::{Approximation, DivEuclid, DivRem, DivRemEuclid, Inverse, RemEuclid, Sign};
11use dashu_int::{fast_div::ConstDivisor, modular::IntoRing, IBig, UBig};
12
13fn make_div_repr<const B: Word>(
17 sign_negative: bool,
18 significand: IBig,
19 exponent: isize,
20) -> Repr<B> {
21 if significand.is_zero() {
22 if sign_negative {
23 Repr::neg_zero()
24 } else {
25 Repr::zero()
26 }
27 } else {
28 Repr::new(significand, exponent)
29 }
30}
31
32macro_rules! impl_div_for_fbig {
33 (impl $op:ident, $method:ident, $repr_method:ident) => {
34 impl<R: Round, const B: Word> $op<FBig<R, B>> for FBig<R, B> {
35 type Output = FBig<R, B>;
36 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
37 let context = Context::max(self.context, rhs.context);
38 let rounded = context.unwrap_fp_repr(context.$repr_method(self.repr, rhs.repr));
39 FBig::new(rounded, context)
40 }
41 }
42
43 impl<'l, R: Round, const B: Word> $op<FBig<R, B>> for &'l FBig<R, B> {
44 type Output = FBig<R, B>;
45 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
46 let context = Context::max(self.context, rhs.context);
47 let rounded =
48 context.unwrap_fp_repr(context.$repr_method(self.repr.clone(), rhs.repr));
49 FBig::new(rounded, context)
50 }
51 }
52
53 impl<'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for FBig<R, B> {
54 type Output = FBig<R, B>;
55 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
56 let context = Context::max(self.context, rhs.context);
57 let rounded =
58 context.unwrap_fp_repr(context.$repr_method(self.repr, rhs.repr.clone()));
59 FBig::new(rounded, context)
60 }
61 }
62
63 impl<'l, 'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for &'l FBig<R, B> {
64 type Output = FBig<R, B>;
65 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
66 let context = Context::max(self.context, rhs.context);
67 let rounded = context
68 .unwrap_fp_repr(context.$repr_method(self.repr.clone(), rhs.repr.clone()));
69 FBig::new(rounded, context)
70 }
71 }
72 };
73}
74
75macro_rules! impl_rem_for_fbig {
76 (impl $op:ident, $method:ident, $repr_method:ident) => {
77 impl<R: Round, const B: Word> $op<FBig<R, B>> for FBig<R, B> {
78 type Output = FBig<R, B>;
79 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
80 let context = Context::max(self.context, rhs.context);
81 FBig::new(context.$repr_method(self.repr, rhs.repr).value(), context)
82 }
83 }
84
85 impl<'l, R: Round, const B: Word> $op<FBig<R, B>> for &'l FBig<R, B> {
86 type Output = FBig<R, B>;
87 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
88 let context = Context::max(self.context, rhs.context);
89 FBig::new(context.$repr_method(self.repr.clone(), rhs.repr).value(), context)
90 }
91 }
92
93 impl<'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for FBig<R, B> {
94 type Output = FBig<R, B>;
95 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
96 let context = Context::max(self.context, rhs.context);
97 FBig::new(context.$repr_method(self.repr, rhs.repr.clone()).value(), context)
98 }
99 }
100
101 impl<'l, 'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for &'l FBig<R, B> {
102 type Output = FBig<R, B>;
103 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
104 let context = Context::max(self.context, rhs.context);
105 FBig::new(
106 context
107 .$repr_method(self.repr.clone(), rhs.repr.clone())
108 .value(),
109 context,
110 )
111 }
112 }
113 };
114}
115impl_div_for_fbig!(impl Div, div, repr_div);
116impl_rem_for_fbig!(impl Rem, rem, repr_rem);
117impl_binop_assign_by_taking!(impl DivAssign<Self>, div_assign, div);
118impl_binop_assign_by_taking!(impl RemAssign<Self>, rem_assign, rem);
119
120impl<R: Round, const B: Word> DivEuclid<FBig<R, B>> for FBig<R, B> {
121 type Output = IBig;
122 #[inline]
123 fn div_euclid(self, rhs: FBig<R, B>) -> Self::Output {
124 let (num, den) = align_as_int(self, rhs);
125 num.div_euclid(den)
126 }
127}
128
129impl<R: Round, const B: Word> DivEuclid<FBig<R, B>> for &FBig<R, B> {
130 type Output = IBig;
131 #[inline]
132 fn div_euclid(self, rhs: FBig<R, B>) -> Self::Output {
133 self.clone().div_euclid(rhs)
134 }
135}
136
137impl<R: Round, const B: Word> DivEuclid<&FBig<R, B>> for FBig<R, B> {
138 type Output = IBig;
139 #[inline]
140 fn div_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
141 self.div_euclid(rhs.clone())
142 }
143}
144
145impl<R: Round, const B: Word> DivEuclid<&FBig<R, B>> for &FBig<R, B> {
146 type Output = IBig;
147 #[inline]
148 fn div_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
149 self.clone().div_euclid(rhs.clone())
150 }
151}
152
153impl<R: Round, const B: Word> RemEuclid<FBig<R, B>> for FBig<R, B> {
154 type Output = FBig<R, B>;
155 #[inline]
156 fn rem_euclid(self, rhs: FBig<R, B>) -> Self::Output {
157 let r_exponent = self.repr.exponent.min(rhs.repr.exponent);
158 let context = Context::max(self.context, rhs.context);
159
160 let (num, den) = align_as_int(self, rhs);
161 let r = num.rem_euclid(den);
162 let mut r = context.convert_int(r.into()).value();
163 if !r.repr.significand.is_zero() {
164 r.repr.exponent += r_exponent;
165 }
166 r
167 }
168}
169
170impl<R: Round, const B: Word> RemEuclid<FBig<R, B>> for &FBig<R, B> {
171 type Output = FBig<R, B>;
172 #[inline]
173 fn rem_euclid(self, rhs: FBig<R, B>) -> Self::Output {
174 self.clone().rem_euclid(rhs)
175 }
176}
177
178impl<R: Round, const B: Word> RemEuclid<&FBig<R, B>> for FBig<R, B> {
179 type Output = FBig<R, B>;
180 #[inline]
181 fn rem_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
182 self.rem_euclid(rhs.clone())
183 }
184}
185
186impl<R: Round, const B: Word> RemEuclid<&FBig<R, B>> for &FBig<R, B> {
187 type Output = FBig<R, B>;
188 #[inline]
189 fn rem_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
190 self.clone().rem_euclid(rhs.clone())
191 }
192}
193
194impl<R: Round, const B: Word> DivRemEuclid<FBig<R, B>> for FBig<R, B> {
195 type OutputDiv = IBig;
196 type OutputRem = FBig<R, B>;
197 #[inline]
198 fn div_rem_euclid(self, rhs: FBig<R, B>) -> (IBig, FBig<R, B>) {
199 let r_exponent = self.repr.exponent.min(rhs.repr.exponent);
200 let context = Context::max(self.context, rhs.context);
201
202 let (num, den) = align_as_int(self, rhs);
203 let (q, r) = num.div_rem_euclid(den);
204 let mut r = context.convert_int(r.into()).value();
205 if !r.repr.significand.is_zero() {
206 r.repr.exponent += r_exponent;
207 }
208 (q, r)
209 }
210}
211
212impl<R: Round, const B: Word> DivRemEuclid<FBig<R, B>> for &FBig<R, B> {
213 type OutputDiv = IBig;
214 type OutputRem = FBig<R, B>;
215 #[inline]
216 fn div_rem_euclid(self, rhs: FBig<R, B>) -> (IBig, FBig<R, B>) {
217 self.clone().div_rem_euclid(rhs)
218 }
219}
220
221impl<R: Round, const B: Word> DivRemEuclid<&FBig<R, B>> for FBig<R, B> {
222 type OutputDiv = IBig;
223 type OutputRem = FBig<R, B>;
224 #[inline]
225 fn div_rem_euclid(self, rhs: &FBig<R, B>) -> (IBig, FBig<R, B>) {
226 self.div_rem_euclid(rhs.clone())
227 }
228}
229
230impl<R: Round, const B: Word> DivRemEuclid<&FBig<R, B>> for &FBig<R, B> {
231 type OutputDiv = IBig;
232 type OutputRem = FBig<R, B>;
233 #[inline]
234 fn div_rem_euclid(self, rhs: &FBig<R, B>) -> (IBig, FBig<R, B>) {
235 self.clone().div_rem_euclid(rhs.clone())
236 }
237}
238
239macro_rules! impl_div_primitive_with_fbig {
240 ($($t:ty)*) => {$(
241 helper_macros::impl_binop_with_primitive!(impl Div<$t>, div);
242 helper_macros::impl_binop_assign_with_primitive!(impl DivAssign<$t>, div_assign);
243 )*};
244}
245impl_div_primitive_with_fbig!(u8 u16 u32 u64 u128 usize UBig i8 i16 i32 i64 i128 isize IBig);
246impl<R: Round, const B: Word> Inverse for FBig<R, B> {
249 type Output = FBig<R, B>;
250
251 #[inline]
252 fn inv(self) -> Self::Output {
253 self.context.unwrap_fp(self.context.inv(&self.repr))
254 }
255}
256
257impl<R: Round, const B: Word> Inverse for &FBig<R, B> {
258 type Output = FBig<R, B>;
259
260 #[inline]
261 fn inv(self) -> Self::Output {
262 self.context.unwrap_fp(self.context.inv(&self.repr))
263 }
264}
265
266impl<R: Round, const B: Word> FBig<R, B> {
267 #[inline]
273 pub fn inv(&self) -> Self {
274 self.context.unwrap_fp(self.context.inv(&self.repr))
275 }
276}
277
278fn align_as_int<R: Round, const B: Word>(lhs: FBig<R, B>, rhs: FBig<R, B>) -> (IBig, IBig) {
280 let ediff = lhs.repr.exponent - rhs.repr.exponent;
281 let (mut num, mut den) = (lhs.repr.significand, rhs.repr.significand);
282 if ediff >= 0 {
283 shl_digits_in_place::<B>(&mut num, ediff as _);
284 } else {
285 shl_digits_in_place::<B>(&mut den, (-ediff) as _);
286 }
287 (num, den)
288}
289
290impl<R: Round> Context<R> {
291 pub(crate) fn repr_div<const B: Word>(&self, lhs: Repr<B>, rhs: Repr<B>) -> FpResult<Repr<B>> {
292 assert_finite_operands(&lhs, &rhs);
293 assert_limited_precision(self.precision);
294
295 let sign_negative = lhs.sign() != rhs.sign();
296 let sign = if sign_negative {
297 Sign::Negative
298 } else {
299 Sign::Positive
300 };
301
302 if rhs.significand.is_zero() {
303 if lhs.significand.is_zero() {
304 } else {
307 return Ok(Approximation::Exact(Repr::infinity_with_sign(sign)));
309 }
310 }
311
312 debug_assert!(lhs.digits() <= self.precision + rhs.digits());
314
315 let (mut q, mut r) = lhs.significand.div_rem(&rhs.significand);
316 let mut e = lhs.exponent.checked_sub(rhs.exponent).ok_or({
317 if lhs.exponent >= 0 {
318 FpError::Overflow(sign)
319 } else {
320 FpError::Underflow(sign)
321 }
322 })?;
323 if r.is_zero() {
324 return Ok(Approximation::Exact(
325 make_div_repr(sign_negative, q, e).check_finite_exponent()?,
326 ));
327 }
328
329 let ddigits = digit_len::<B>(&rhs.significand);
330 if q.is_zero() {
331 let rdigits = digit_len::<B>(&r); let shift = ddigits + self.precision - rdigits;
334 shl_digits_in_place::<B>(&mut r, shift);
335 e = e
336 .checked_sub(shift as isize)
337 .ok_or(FpError::Underflow(sign))?;
338 let (q0, r0) = r.div_rem(&rhs.significand);
339 q = q0;
340 r = r0;
341 } else {
342 let ndigits = digit_len::<B>(&q) + ddigits;
343 if ndigits < ddigits + self.precision {
344 let shift = ddigits + self.precision - ndigits;
346 shl_digits_in_place::<B>(&mut q, shift);
347 shl_digits_in_place::<B>(&mut r, shift);
348 e = e
349 .checked_sub(shift as isize)
350 .ok_or(FpError::Underflow(sign))?;
351
352 let (q0, r0) = r.div_rem(&rhs.significand);
353 q += q0;
354 r = r0;
355 }
356 }
357
358 let repr = if r.is_zero() {
359 Approximation::Exact(make_div_repr(sign_negative, q, e))
360 } else {
361 let adjust = R::round_ratio(&q, r, &rhs.significand);
362 Approximation::Inexact(make_div_repr(sign_negative, q + adjust, e), adjust)
363 };
364 Ok(repr)
365 }
366
367 pub(crate) fn repr_rem<const B: Word>(&self, lhs: Repr<B>, rhs: Repr<B>) -> Rounded<Repr<B>> {
368 assert_finite_operands(&lhs, &rhs);
369
370 let lhs_is_neg_zero = lhs.is_neg_zero();
371 let (lhs_sign, lhs_signif) = lhs.significand.into_parts();
372 let (_, rhs_signif) = rhs.significand.into_parts();
373
374 use core::cmp::Ordering;
375 let significand = match lhs.exponent.cmp(&rhs.exponent) {
376 Ordering::Equal => {
377 let r1 = lhs_signif % &rhs_signif;
378 let r2 = rhs_signif - &r1;
379 if r1 < r2 {
380 IBig::from_parts(lhs_sign, r1)
381 } else {
382 IBig::from_parts(-lhs_sign, r2)
383 }
384 }
385 Ordering::Greater => {
386 let modulo = ConstDivisor::new(rhs_signif);
389 let shift = (lhs.exponent - rhs.exponent) as usize;
390 let scaling = if B == 2 {
391 (UBig::ONE << shift).into_ring(&modulo)
392 } else {
393 UBig::from_word(B).into_ring(&modulo).pow(&shift.into())
394 };
395 let r = lhs_signif.into_ring(&modulo) * scaling;
396 let r1 = r.residue();
397 let r2 = (-r).residue();
398 if r1 < r2 {
399 IBig::from_parts(lhs_sign, r1)
400 } else {
401 IBig::from_parts(-lhs_sign, r2)
402 }
403 }
404 Ordering::Less => {
405 let shift = (rhs.exponent - lhs.exponent) as usize;
407 let (hi, lo) = split_digits::<B>(lhs_signif.into(), shift);
408
409 let mut r1 = hi % &rhs_signif;
410 let mut r2 = rhs_signif - &r1;
411
412 shl_digits_in_place::<B>(&mut r1, shift);
413 r1 += &lo;
414
415 shl_digits_in_place::<B>(&mut r2, shift);
416 r2 -= lo;
417
418 if r1 < r2 {
419 lhs_sign * r1
420 } else {
421 (-lhs_sign) * r2
422 }
423 }
424 };
425
426 let exponent = lhs.exponent.min(rhs.exponent);
427 if significand.is_zero() {
428 Approximation::Exact(if lhs_is_neg_zero {
430 Repr::neg_zero()
431 } else {
432 Repr::zero()
433 })
434 } else {
435 match Repr::new(significand, exponent).check_finite_exponent() {
436 Ok(repr) => self.repr_round(repr),
437 Err(e) => match e {
438 FpError::Overflow(sign) => {
439 Approximation::Inexact(Repr::infinity_with_sign(sign), Rounding::NoOp)
440 }
441 FpError::Underflow(sign) => {
442 Approximation::Inexact(Repr::zero_with_sign(sign), Rounding::NoOp)
443 }
444 _ => unreachable!(),
445 },
446 }
447 }
448 }
449
450 pub fn div<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
474 if lhs.is_infinite() || rhs.is_infinite() {
475 return Err(FpError::InfiniteInput);
476 }
477 if lhs.significand.is_zero() && rhs.significand.is_zero() {
478 return Err(FpError::Indeterminate); }
480
481 let lhs_repr = if !lhs.is_pos_zero() && lhs.digits_ub() > rhs.digits_lb() + self.precision {
482 Self::new(rhs.digits() + self.precision)
484 .repr_round_ref(lhs)
485 .value()
486 } else {
487 lhs.clone()
488 };
489 Ok(self
490 .repr_div(lhs_repr, rhs.clone())?
491 .map(|v| FBig::new(v, *self)))
492 }
493
494 pub fn rem<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
515 if lhs.is_infinite() || rhs.is_infinite() {
516 return Err(FpError::InfiniteInput);
517 }
518 Ok(self
519 .repr_rem(lhs.clone(), rhs.clone())
520 .map(|v| FBig::new(v, *self)))
521 }
522
523 #[inline]
538 pub fn inv<const B: Word>(&self, f: &Repr<B>) -> FpResult<FBig<R, B>> {
539 if f.is_infinite() {
540 return Err(FpError::InfiniteInput);
541 }
542 Ok(self
544 .repr_div(Repr::one(), f.clone())?
545 .map(|v| FBig::new(v, *self)))
546 }
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552 use crate::round::mode;
553
554 fn r2(sig: i32, exp: isize) -> Repr<2> {
555 Repr::new(sig.into(), exp)
556 }
557
558 #[test]
559 fn test_div_by_zero_is_infinity() {
560 let ctx = Context::<mode::HalfEven>::new(53);
561 let pos = ctx.div::<2>(&r2(1, 0), &Repr::<2>::zero()).unwrap().value();
563 assert!(pos.repr().is_infinite());
564 assert_eq!(pos.repr().sign(), Sign::Positive);
565
566 let neg = ctx
567 .div::<2>(&r2(-1, 0), &Repr::<2>::zero())
568 .unwrap()
569 .value();
570 assert_eq!(neg.repr().sign(), Sign::Negative);
571
572 let neg2 = ctx
574 .div::<2>(&r2(1, 0), &Repr::<2>::neg_zero())
575 .unwrap()
576 .value();
577 assert_eq!(neg2.repr().sign(), Sign::Negative);
578 }
579
580 #[test]
581 fn test_zero_over_zero_is_indeterminate() {
582 let ctx = Context::<mode::HalfEven>::new(53);
583 assert_eq!(
584 ctx.div::<2>(&Repr::<2>::zero(), &Repr::<2>::zero()),
585 Err(FpError::Indeterminate)
586 );
587 }
588
589 #[test]
590 fn test_inv_zero_is_infinity() {
591 let ctx = Context::<mode::HalfEven>::new(53);
592 let r = ctx.inv::<2>(&Repr::<2>::zero()).unwrap().value();
593 assert!(r.repr().is_infinite());
594 assert_eq!(r.repr().sign(), Sign::Positive);
595 }
596
597 #[test]
598 fn test_fbig_div_zero_produces_infinity() {
599 let one = FBig::<mode::HalfEven>::try_from(1.0f64).unwrap();
601 let zero = FBig::<mode::HalfEven>::try_from(0.0f64).unwrap();
602 let inf = one / zero;
603 assert!(inf.repr().is_infinite());
604 }
605
606 #[test]
607 #[should_panic]
608 fn test_fbig_zero_over_zero_panics() {
609 let zero = FBig::<mode::HalfEven>::try_from(0.0f64).unwrap();
611 let _ = zero.clone() / zero;
612 }
613}