Skip to main content

rug/float/
traits.rs

1// Copyright © 2016–2026 Trevor Spiteri
2
3// This program is free software: you can redistribute it and/or modify it under
4// the terms of the GNU Lesser General Public License as published by the Free
5// Software Foundation, either version 3 of the License, or (at your option) any
6// later version.
7//
8// This program is distributed in the hope that it will be useful, but WITHOUT
9// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11// details.
12//
13// You should have received a copy of the GNU Lesser General Public License and
14// a copy of the GNU General Public License along with this program. If not, see
15// <https://www.gnu.org/licenses/>.
16
17#[cfg(feature = "integer")]
18use crate::Integer;
19use crate::ext::xmpfr;
20#[allow(deprecated)]
21use crate::float::SmallFloat;
22use crate::float::big;
23use crate::float::big::{ExpFormat, Format};
24use crate::float::{Constant, MiniFloat, OrdFloat, Round, Special};
25use crate::misc::StringLike;
26use crate::ops::AssignRound;
27use crate::{Assign, Float};
28#[cfg(feature = "rational")]
29use crate::{Rational, rational::TryFromFloatError};
30#[cfg(feature = "rational")]
31use az::CheckedCast;
32use az::{StrictAs, StrictCast};
33use core::cmp::Ordering;
34use core::fmt::{
35    Binary, Debug, Display, Formatter, LowerExp, LowerHex, Octal, Result as FmtResult, UpperExp,
36    UpperHex,
37};
38use gmp_mpfr_sys::mpfr;
39
40impl Clone for Float {
41    #[inline]
42    fn clone(&self) -> Float {
43        let mut ret = Float::new_nan(self.prec());
44        if !self.is_nan() {
45            ret.assign(self);
46        }
47        ret
48    }
49
50    #[inline]
51    fn clone_from(&mut self, source: &Float) {
52        xmpfr::set_prec_nan(self, source.prec().strict_cast());
53        if !source.is_nan() {
54            self.assign(source);
55        }
56    }
57}
58
59impl Drop for Float {
60    #[inline]
61    fn drop(&mut self) {
62        // Safety: we are freeing memory. This is sound as self must be initialized.
63        unsafe {
64            mpfr::clear(self.as_raw_mut());
65        }
66    }
67}
68
69impl Display for Float {
70    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
71        let format = Format {
72            exp: ExpFormat::Point,
73            ..Format::default()
74        };
75        fmt_radix(self, f, format, "")
76    }
77}
78
79impl Debug for Float {
80    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
81        let format = Format {
82            exp: ExpFormat::Point,
83            ..Format::default()
84        };
85        fmt_radix(self, f, format, "")
86    }
87}
88
89impl LowerExp for Float {
90    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
91        let format = Format {
92            exp: ExpFormat::Exp,
93            ..Format::default()
94        };
95        fmt_radix(self, f, format, "")
96    }
97}
98
99impl UpperExp for Float {
100    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
101        let format = Format {
102            to_upper: true,
103            exp: ExpFormat::Exp,
104            ..Format::default()
105        };
106        fmt_radix(self, f, format, "")
107    }
108}
109
110impl Binary for Float {
111    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
112        let format = Format {
113            radix: 2,
114            exp: ExpFormat::Point,
115            ..Format::default()
116        };
117        fmt_radix(self, f, format, "0b")
118    }
119}
120
121impl Octal for Float {
122    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
123        let format = Format {
124            radix: 8,
125            exp: ExpFormat::Point,
126            ..Format::default()
127        };
128        fmt_radix(self, f, format, "0o")
129    }
130}
131
132impl LowerHex for Float {
133    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
134        let format = Format {
135            radix: 16,
136            exp: ExpFormat::Point,
137            ..Format::default()
138        };
139        fmt_radix(self, f, format, "0x")
140    }
141}
142
143impl UpperHex for Float {
144    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
145        let format = Format {
146            radix: 16,
147            to_upper: true,
148            exp: ExpFormat::Point,
149            ..Format::default()
150        };
151        fmt_radix(self, f, format, "0x")
152    }
153}
154
155impl AsRef<OrdFloat> for Float {
156    #[inline]
157    fn as_ref(&self) -> &OrdFloat {
158        self.as_ord()
159    }
160}
161
162impl<T> Assign<T> for Float
163where
164    Self: AssignRound<T, Round = Round, Ordering = Ordering>,
165{
166    #[inline]
167    fn assign(&mut self, src: T) {
168        self.assign_round(src, Round::Nearest);
169    }
170}
171
172impl AssignRound<Constant> for Float {
173    type Round = Round;
174    type Ordering = Ordering;
175    #[inline]
176    fn assign_round(&mut self, src: Constant, round: Round) -> Ordering {
177        match src {
178            Constant::Log2 => xmpfr::const_log2(self, round),
179            Constant::Pi => xmpfr::const_pi(self, round),
180            Constant::Euler => xmpfr::const_euler(self, round),
181            Constant::Catalan => xmpfr::const_catalan(self, round),
182        }
183    }
184}
185
186assign_round_deref! { Constant => Float }
187
188impl AssignRound<Special> for Float {
189    type Round = Round;
190    type Ordering = Ordering;
191    #[inline]
192    fn assign_round(&mut self, src: Special, _round: Round) -> Ordering {
193        xmpfr::set_special(self, src);
194        Ordering::Equal
195    }
196}
197
198assign_round_deref! { Special => Float }
199
200impl AssignRound for Float {
201    type Round = Round;
202    type Ordering = Ordering;
203    #[inline]
204    fn assign_round(&mut self, src: Float, round: Round) -> Ordering {
205        if self.prec() == src.prec() {
206            *self = src;
207            if self.is_nan() {
208                xmpfr::set_nanflag();
209            }
210            Ordering::Equal
211        } else {
212            self.assign_round(&src, round)
213        }
214    }
215}
216
217impl AssignRound<&Float> for Float {
218    type Round = Round;
219    type Ordering = Ordering;
220    #[inline]
221    fn assign_round(&mut self, src: &Float, round: Round) -> Ordering {
222        xmpfr::set(self, src, round)
223    }
224}
225
226impl AssignRound<MiniFloat> for Float {
227    type Round = Round;
228    type Ordering = Ordering;
229    #[inline]
230    fn assign_round(&mut self, mut src: MiniFloat, round: Round) -> Ordering {
231        self.assign_round(src.borrow_excl(), round)
232    }
233}
234
235impl AssignRound<&MiniFloat> for Float {
236    type Round = Round;
237    type Ordering = Ordering;
238    #[inline]
239    fn assign_round(&mut self, src: &MiniFloat, round: Round) -> Ordering {
240        self.assign_round(&*src.borrow(), round)
241    }
242}
243
244#[allow(deprecated)]
245impl AssignRound<SmallFloat> for Float {
246    type Round = Round;
247    type Ordering = Ordering;
248    #[inline]
249    fn assign_round(&mut self, src: SmallFloat, round: Round) -> Ordering {
250        self.assign_round(&*src, round)
251    }
252}
253
254#[allow(deprecated)]
255impl AssignRound<&SmallFloat> for Float {
256    type Round = Round;
257    type Ordering = Ordering;
258    #[inline]
259    fn assign_round(&mut self, src: &SmallFloat, round: Round) -> Ordering {
260        self.assign_round(&**src, round)
261    }
262}
263
264#[cfg(feature = "integer")]
265macro_rules! assign {
266    ($T:ty, $func:path) => {
267        impl AssignRound<&$T> for Float {
268            type Round = Round;
269            type Ordering = Ordering;
270            #[inline]
271            fn assign_round(&mut self, src: &$T, round: Round) -> Ordering {
272                $func(self, src, round)
273            }
274        }
275
276        impl AssignRound<$T> for Float {
277            type Round = Round;
278            type Ordering = Ordering;
279            #[inline]
280            fn assign_round(&mut self, src: $T, round: Round) -> Ordering {
281                self.assign_round(&src, round)
282            }
283        }
284    };
285}
286
287#[cfg(feature = "integer")]
288assign! { Integer, xmpfr::set_z }
289#[cfg(feature = "rational")]
290assign! { Rational, xmpfr::set_q }
291
292macro_rules! conv_ops {
293    ($T:ty, $set:path) => {
294        impl AssignRound<$T> for Float {
295            type Round = Round;
296            type Ordering = Ordering;
297            #[inline]
298            fn assign_round(&mut self, src: $T, round: Round) -> Ordering {
299                $set(self, src.into(), round)
300            }
301        }
302
303        assign_round_deref! { $T => Float }
304    };
305}
306
307macro_rules! conv_ops_cast {
308    ($New:ty, $Existing:ty) => {
309        impl AssignRound<$New> for Float {
310            type Round = Round;
311            type Ordering = Ordering;
312            #[inline]
313            fn assign_round(&mut self, src: $New, round: Round) -> Ordering {
314                self.assign_round(src.strict_as::<$Existing>(), round)
315            }
316        }
317
318        assign_round_deref! { $New => Float }
319    };
320}
321
322conv_ops! { i8, xmpfr::set_si }
323conv_ops! { i16, xmpfr::set_si }
324conv_ops! { i32, xmpfr::set_si }
325conv_ops! { i64, xmpfr::set_sj }
326conv_ops! { i128, xmpfr::set_i128 }
327#[cfg(target_pointer_width = "32")]
328conv_ops_cast! { isize, i32 }
329#[cfg(target_pointer_width = "64")]
330conv_ops_cast! { isize, i64 }
331
332conv_ops! { bool, xmpfr::set_ui }
333conv_ops! { u8, xmpfr::set_ui }
334conv_ops! { u16, xmpfr::set_ui }
335conv_ops! { u32, xmpfr::set_ui }
336conv_ops! { u64, xmpfr::set_uj }
337conv_ops! { u128, xmpfr::set_u128 }
338#[cfg(target_pointer_width = "32")]
339conv_ops_cast! { usize, u32 }
340#[cfg(target_pointer_width = "64")]
341conv_ops_cast! { usize, u64 }
342
343#[cfg(feature = "nightly-float")]
344conv_ops! { f16, xmpfr::set_f16 }
345conv_ops! { f32, xmpfr::set_f32 }
346conv_ops! { f64, xmpfr::set_f64 }
347#[cfg(feature = "nightly-float")]
348conv_ops! { f128, xmpfr::set_f128 }
349
350#[cfg(feature = "rational")]
351impl TryFrom<Float> for Rational {
352    type Error = TryFromFloatError;
353    #[inline]
354    fn try_from(value: Float) -> Result<Self, TryFromFloatError> {
355        TryFrom::try_from(&value)
356    }
357}
358
359#[cfg(feature = "rational")]
360impl TryFrom<&Float> for Rational {
361    type Error = TryFromFloatError;
362    #[inline]
363    fn try_from(value: &Float) -> Result<Self, TryFromFloatError> {
364        value
365            .checked_cast()
366            .ok_or(TryFromFloatError { _unused: () })
367    }
368}
369
370// overwrites format.precision
371fn fmt_radix(flt: &Float, fmt: &mut Formatter<'_>, format: Format, prefix: &str) -> FmtResult {
372    let format = Format {
373        precision: fmt.precision(),
374        ..format
375    };
376    let mut s = StringLike::new_malloc();
377    big::append_to_string(&mut s, flt, format);
378    let st = s.as_str();
379    let (neg, buf) = if let Some(stripped) = st.strip_prefix('-') {
380        (true, stripped)
381    } else {
382        (false, st)
383    };
384    let prefix = if flt.is_finite() { prefix } else { "" };
385    fmt.pad_integral(!neg, prefix, buf)
386}
387
388// Safety: mpfr_t is thread safe as guaranteed by the MPFR library.
389unsafe impl Send for Float {}
390unsafe impl Sync for Float {}
391
392#[cfg(test)]
393#[allow(clippy::float_cmp)]
394mod tests {
395    use crate::float;
396    use crate::float::{FreeCache, Round};
397    use crate::ops::AssignRound;
398    use crate::{Assign, Float};
399    use core::cmp::Ordering;
400
401    #[test]
402    fn check_assign() {
403        let mut f = Float::with_val(4, 1.0);
404        assert_eq!(f, 1.0);
405
406        let other = Float::with_val(53, 14.75);
407        let mut dir = f.assign_round(&other, Round::Nearest);
408        assert_eq!(f, 15.0);
409        assert_eq!(dir, Ordering::Greater);
410
411        dir = f.assign_round(14.25, Round::Nearest);
412        assert_eq!(f, 14.0);
413        assert_eq!(dir, Ordering::Less);
414
415        f.assign(other);
416        assert_eq!(f, 15.0);
417
418        float::free_cache(FreeCache::All);
419    }
420
421    #[cfg(feature = "rational")]
422    #[test]
423    fn check_fallible_conversions() {
424        use crate::float::Special;
425        use crate::{Float, Rational};
426        let large = [
427            Float::with_val(20, Special::Zero),
428            Float::with_val(20, Special::NegZero),
429            Float::with_val(20, Special::Infinity),
430            Float::with_val(20, Special::NegInfinity),
431            Float::with_val(20, Special::Nan),
432            Float::with_val(20, 1),
433            Float::with_val(20, -1),
434            Float::with_val(20, 999_999e100),
435            Float::with_val(20, 999_999e-100),
436            Float::with_val(20, -999_999e100),
437            Float::with_val(20, -999_999e-100),
438        ];
439        for f in &large {
440            let r = Rational::try_from(f);
441            assert_eq!(r.is_ok(), f.is_finite());
442            if let Ok(r) = r {
443                assert_eq!(r, *f);
444            }
445        }
446
447        float::free_cache(FreeCache::All);
448    }
449}