Skip to main content

oxinum_float/native/
float_mul.rs

1//! Multiplication for `BigFloat`.
2//!
3//! Strategy: multiply the mantissas as plain [`BigUint`]s (which dispatches to
4//! Karatsuba above the threshold and to schoolbook below), sum the exponents
5//! (saturating, matching the convention used by addition's
6//! `round_to_precision_in_place`), XOR the signs, and feed the parts through
7//! [`BigFloat::from_parts`] to land at the canonical form at the target
8//! precision `max(p_a, p_b)` under the chosen rounding mode.
9//!
10//! The default operator (`a * b`) uses [`RoundingMode::HalfEven`] — the same
11//! choice the addition operator makes.
12
13use core::ops::{Mul, MulAssign};
14
15use oxinum_core::Sign;
16
17use super::float::{BigFloat, RoundingMode};
18use super::nonfinite::{nonfinite_propagate, BinOp};
19
20impl BigFloat {
21    /// Return `self * other`, rounding the result to
22    /// `max(self.precision, other.precision)` using banker's rounding.
23    pub fn mul_ref(&self, other: &BigFloat) -> BigFloat {
24        self.mul_ref_with_mode(other, RoundingMode::HalfEven)
25    }
26
27    /// Return `self * other`, rounding the result to
28    /// `max(self.precision, other.precision)` using the chosen rounding mode.
29    pub fn mul_ref_with_mode(&self, other: &BigFloat, mode: RoundingMode) -> BigFloat {
30        // Non-finite fast-path: propagate NaN/Inf per IEEE 754 (Inf*0=NaN, etc.).
31        if let Some(result) = nonfinite_propagate(self, other, BinOp::Mul) {
32            return result;
33        }
34        let target_prec = self.precision.max(other.precision);
35        // Either operand zero -> canonical zero at the target precision.
36        if self.is_zero() || other.is_zero() {
37            return BigFloat::zero(target_prec);
38        }
39        // Result sign: equal signs -> Positive, opposite signs -> Negative.
40        let out_sign = if self.sign == other.sign {
41            Sign::Positive
42        } else {
43            Sign::Negative
44        };
45        // Mantissa product — Karatsuba above threshold via BigUint Mul.
46        let out_mantissa = &self.mantissa * &other.mantissa;
47        // Exponent sum — saturating, mirroring `round_to_precision_in_place`'s
48        // approach to far-from-the-bound i64 exponent arithmetic.
49        let out_exponent = self.exponent.saturating_add(other.exponent);
50        BigFloat::from_parts(out_sign, out_mantissa, out_exponent, target_prec, mode)
51    }
52}
53
54// ---------------------------------------------------------------------------
55// Operator impls — owned and borrowed (matches the pattern in float_add.rs)
56// ---------------------------------------------------------------------------
57
58impl Mul<&BigFloat> for &BigFloat {
59    type Output = BigFloat;
60    #[inline]
61    fn mul(self, rhs: &BigFloat) -> BigFloat {
62        self.mul_ref(rhs)
63    }
64}
65
66impl Mul<BigFloat> for BigFloat {
67    type Output = BigFloat;
68    #[inline]
69    fn mul(self, rhs: BigFloat) -> BigFloat {
70        self.mul_ref(&rhs)
71    }
72}
73
74impl Mul<&BigFloat> for BigFloat {
75    type Output = BigFloat;
76    #[inline]
77    fn mul(self, rhs: &BigFloat) -> BigFloat {
78        self.mul_ref(rhs)
79    }
80}
81
82impl Mul<BigFloat> for &BigFloat {
83    type Output = BigFloat;
84    #[inline]
85    fn mul(self, rhs: BigFloat) -> BigFloat {
86        self.mul_ref(&rhs)
87    }
88}
89
90impl MulAssign<&BigFloat> for BigFloat {
91    #[inline]
92    fn mul_assign(&mut self, rhs: &BigFloat) {
93        *self = self.mul_ref(rhs);
94    }
95}
96
97impl MulAssign<BigFloat> for BigFloat {
98    #[inline]
99    fn mul_assign(&mut self, rhs: BigFloat) {
100        *self = self.mul_ref(&rhs);
101    }
102}