Skip to main content

dashu_float/
mul.rs

1use dashu_base::Sign;
2use dashu_int::{IBig, UBig};
3
4use crate::{
5    error::{assert_finite_operands, FpError, FpResult},
6    fbig::FBig,
7    helper_macros,
8    repr::{Context, Repr, Word},
9    round::Round,
10};
11use core::ops::{Mul, MulAssign};
12
13/// Raw product of two finite reprs, attaching the XOR sign of the operands to a zero product
14/// (the significand product alone is `+0`, losing the sign).
15///
16/// Returns an error when the result exponent overflows or underflows `isize`.
17fn make_mul_repr<const B: Word>(lhs: &Repr<B>, rhs: &Repr<B>) -> Result<Repr<B>, FpError> {
18    let significand = &lhs.significand * &rhs.significand;
19    if significand.is_zero() {
20        return Ok(if lhs.sign() != rhs.sign() {
21            Repr::neg_zero()
22        } else {
23            Repr::zero()
24        });
25    }
26    let sign = if lhs.sign() != rhs.sign() {
27        Sign::Negative
28    } else {
29        Sign::Positive
30    };
31    let exponent = lhs.exponent.checked_add(rhs.exponent).ok_or_else(|| {
32        debug_assert!(
33            lhs.exponent.is_positive() == rhs.exponent.is_positive(),
34            "checked_add overflow with mixed-sign exponents is impossible"
35        );
36        if lhs.exponent > 0 {
37            FpError::Overflow(sign)
38        } else {
39            FpError::Underflow(sign)
40        }
41    })?;
42    Repr::new(significand, exponent).check_finite_exponent()
43}
44
45macro_rules! unwrap_mul_repr {
46    ($result:expr, $context:expr) => {
47        match $result {
48            Ok(r) => r,
49            Err(FpError::Overflow(sign)) => {
50                return FBig::new(Repr::infinity_with_sign(sign), $context);
51            }
52            Err(FpError::Underflow(sign)) => {
53                return FBig::new(Repr::zero_with_sign(sign), $context);
54            }
55            Err(_) => unreachable!(),
56        }
57    };
58}
59
60impl<R: Round, const B: Word> Mul<&FBig<R, B>> for &FBig<R, B> {
61    type Output = FBig<R, B>;
62
63    #[inline]
64    fn mul(self, rhs: &FBig<R, B>) -> Self::Output {
65        assert_finite_operands(&self.repr, &rhs.repr);
66
67        let context = Context::max(self.context, rhs.context);
68        let repr = unwrap_mul_repr!(make_mul_repr(&self.repr, &rhs.repr), context);
69        FBig::new(context.repr_round(repr).value(), context)
70    }
71}
72
73impl<R: Round, const B: Word> Mul<&FBig<R, B>> for FBig<R, B> {
74    type Output = FBig<R, B>;
75
76    #[inline]
77    fn mul(self, rhs: &FBig<R, B>) -> Self::Output {
78        assert_finite_operands(&self.repr, &rhs.repr);
79
80        let context = Context::max(self.context, rhs.context);
81        let repr = unwrap_mul_repr!(make_mul_repr(&self.repr, &rhs.repr), context);
82        FBig::new(context.repr_round(repr).value(), context)
83    }
84}
85
86impl<R: Round, const B: Word> Mul<FBig<R, B>> for &FBig<R, B> {
87    type Output = FBig<R, B>;
88
89    #[inline]
90    fn mul(self, rhs: FBig<R, B>) -> Self::Output {
91        assert_finite_operands(&self.repr, &rhs.repr);
92
93        let context = Context::max(self.context, rhs.context);
94        let repr = unwrap_mul_repr!(make_mul_repr(&self.repr, &rhs.repr), context);
95        FBig::new(context.repr_round(repr).value(), context)
96    }
97}
98
99impl<R: Round, const B: Word> Mul<FBig<R, B>> for FBig<R, B> {
100    type Output = FBig<R, B>;
101
102    #[inline]
103    fn mul(self, rhs: FBig<R, B>) -> Self::Output {
104        assert_finite_operands(&self.repr, &rhs.repr);
105
106        let context = Context::max(self.context, rhs.context);
107        let repr = unwrap_mul_repr!(make_mul_repr(&self.repr, &rhs.repr), context);
108        FBig::new(context.repr_round(repr).value(), context)
109    }
110}
111
112helper_macros::impl_binop_assign_by_taking!(impl MulAssign<Self>, mul_assign, mul);
113
114macro_rules! impl_mul_primitive_with_fbig {
115    ($($t:ty)*) => {$(
116        helper_macros::impl_binop_with_primitive!(impl Mul<$t>, mul);
117        helper_macros::impl_binop_assign_with_primitive!(impl MulAssign<$t>, mul_assign);
118    )*};
119}
120impl_mul_primitive_with_fbig!(u8 u16 u32 u64 u128 usize UBig i8 i16 i32 i64 i128 isize IBig);
121
122impl<R: Round, const B: Word> FBig<R, B> {
123    /// Compute the square of this number (`self * self`)
124    ///
125    /// # Examples
126    ///
127    /// ```
128    /// # use core::str::FromStr;
129    /// # use dashu_base::ParseError;
130    /// # use dashu_float::DBig;
131    /// let a = DBig::from_str("-1.234")?;
132    /// assert_eq!(a.sqr(), DBig::from_str("1.523")?);
133    /// # Ok::<(), ParseError>(())
134    /// ```
135    #[inline]
136    pub fn sqr(&self) -> Self {
137        self.context.unwrap_fp(self.context.sqr(&self.repr))
138    }
139
140    /// Compute the cubic of this number (`self * self * self`)
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// # use core::str::FromStr;
146    /// # use dashu_base::ParseError;
147    /// # use dashu_float::DBig;
148    /// let a = DBig::from_str("-1.234")?;
149    /// assert_eq!(a.cubic(), DBig::from_str("-1.879")?);
150    /// # Ok::<(), ParseError>(())
151    /// ```
152    #[inline]
153    pub fn cubic(&self) -> Self {
154        self.context.unwrap_fp(self.context.cubic(&self.repr))
155    }
156}
157
158impl<R: Round> Context<R> {
159    /// Multiply two floating point numbers under this context.
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// # use core::str::FromStr;
165    /// # use dashu_base::ParseError;
166    /// # use dashu_float::DBig;
167    /// use dashu_base::Approximation::*;
168    /// use dashu_float::{Context, round::{mode::HalfAway, Rounding::*}};
169    ///
170    /// let context = Context::<HalfAway>::new(2);
171    /// let a = DBig::from_str("-1.234")?;
172    /// let b = DBig::from_str("6.789")?;
173    /// assert_eq!(
174    ///     context.mul(&a.repr(), &b.repr()),
175    ///     Ok(Inexact(DBig::from_str("-8.4")?, SubOne))
176    /// );
177    /// # Ok::<(), ParseError>(())
178    /// ```
179    pub fn mul<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
180        if lhs.is_infinite() || rhs.is_infinite() {
181            return Err(FpError::InfiniteInput);
182        }
183
184        // at most double the precision is required to get a correct result
185        // shrink the input operands if necessary
186        let max_precision = if self.is_limited() {
187            self.precision * 2
188        } else {
189            usize::MAX
190        };
191
192        let lhs_shrink;
193        let lhs_repr = if lhs.digits() > max_precision {
194            lhs_shrink = Context::<R>::new(max_precision).repr_round_ref(lhs).value();
195            &lhs_shrink
196        } else {
197            lhs
198        };
199
200        let rhs_shrink;
201        let rhs_repr = if rhs.digits() > max_precision {
202            rhs_shrink = Context::<R>::new(max_precision).repr_round_ref(rhs).value();
203            &rhs_shrink
204        } else {
205            rhs
206        };
207
208        let repr = make_mul_repr(lhs_repr, rhs_repr)?;
209        Ok(self.repr_round(repr).map(|v| FBig::new(v, *self)))
210    }
211
212    /// Calculate the square of the floating point number under this context.
213    ///
214    /// # Examples
215    ///
216    /// ```
217    /// # use core::str::FromStr;
218    /// # use dashu_base::ParseError;
219    /// # use dashu_float::DBig;
220    /// use dashu_base::Approximation::*;
221    /// use dashu_float::{Context, round::{mode::HalfAway, Rounding::*}};
222    ///
223    /// let context = Context::<HalfAway>::new(2);
224    /// let a = DBig::from_str("-1.234")?;
225    /// assert_eq!(context.sqr(&a.repr()), Ok(Inexact(DBig::from_str("1.5")?, NoOp)));
226    /// # Ok::<(), ParseError>(())
227    /// ```
228    pub fn sqr<const B: Word>(&self, f: &Repr<B>) -> FpResult<FBig<R, B>> {
229        if f.is_infinite() {
230            return Err(FpError::InfiniteInput);
231        }
232
233        // shrink the input operands if necessary
234        let max_precision = if self.is_limited() {
235            self.precision * 2
236        } else {
237            usize::MAX
238        };
239
240        let f_shrink;
241        let f_repr = if f.digits() > max_precision {
242            f_shrink = Context::<R>::new(max_precision).repr_round_ref(f).value();
243            &f_shrink
244        } else {
245            f
246        };
247
248        let exponent = f_repr.exponent.checked_mul(2).ok_or({
249            // sqr always produces a non-negative result
250            if f_repr.exponent > 0 {
251                FpError::Overflow(Sign::Positive)
252            } else {
253                FpError::Underflow(Sign::Positive)
254            }
255        })?;
256        let repr = Repr::new(f_repr.significand.sqr().into(), exponent);
257        let repr = repr.check_finite_exponent()?;
258        Ok(self.repr_round(repr).map(|v| FBig::new(v, *self)))
259    }
260
261    /// Calculate the cubic of the floating point number under this context.
262    ///
263    /// # Examples
264    ///
265    /// ```
266    /// # use core::str::FromStr;
267    /// # use dashu_base::ParseError;
268    /// # use dashu_float::DBig;
269    /// use dashu_base::Approximation::*;
270    /// use dashu_float::{Context, round::{mode::HalfAway, Rounding::*}};
271    ///
272    /// let context = Context::<HalfAway>::new(2);
273    /// let a = DBig::from_str("-1.234")?;
274    /// assert_eq!(context.cubic(&a.repr()), Ok(Inexact(DBig::from_str("-1.9")?, SubOne)));
275    /// # Ok::<(), ParseError>(())
276    /// ```
277    pub fn cubic<const B: Word>(&self, f: &Repr<B>) -> FpResult<FBig<R, B>> {
278        if f.is_infinite() {
279            return Err(FpError::InfiniteInput);
280        }
281
282        // shrink the input operands if necessary
283        let max_precision = if self.is_limited() {
284            self.precision * 3
285        } else {
286            usize::MAX
287        };
288
289        let f_shrink;
290        let f_repr = if f.digits() > max_precision {
291            f_shrink = Context::<R>::new(max_precision).repr_round_ref(f).value();
292            &f_shrink
293        } else {
294            f
295        };
296
297        let repr = if f_repr.significand.is_zero() {
298            // cubic(±0) = ±0 (odd power preserves sign)
299            if f_repr.is_neg_zero() {
300                Repr::neg_zero()
301            } else {
302                Repr::zero()
303            }
304        } else {
305            let sign = f_repr.sign();
306            let exponent = f_repr.exponent.checked_mul(3).ok_or({
307                if f_repr.exponent > 0 {
308                    FpError::Overflow(sign)
309                } else {
310                    FpError::Underflow(sign)
311                }
312            })?;
313            let repr = Repr::new(f_repr.significand.cubic(), exponent);
314            repr.check_finite_exponent()?
315        };
316        Ok(self.repr_round(repr).map(|v| FBig::new(v, *self)))
317    }
318}