Skip to main content

malachite_float/
lib.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9//! This crate defines [`Float`]s, which are arbitrary-precision floating-point numbers.
10//!
11//! [`Float`]s are not yet feature-complete, but the functions that are implemented are thoroughly
12//! tested and documented.
13//!
14//! # Demos and benchmarks
15//! This crate comes with a `bin` target that can be used for running demos and benchmarks.
16//! - Almost all of the public functions in this crate have an associated demo. Running a demo
17//!   shows you a function's behavior on a large number of inputs. TODO
18//! - You can use a similar command to run benchmarks. TODO
19//!
20//! The list of available demos and benchmarks is not documented anywhere; you must find them by
21//! browsing through
22//! [`bin_util/demo_and_bench`](https://github.com/mhogrefe/malachite/tree/master/malachite-float/src/bin_util/demo_and_bench).
23//!
24//! # Features
25//! - `32_bit_limbs`: Sets the type of [`Limb`](malachite_nz#limbs) to [`u32`] instead of the
26//!   default, [`u64`].
27//! - `test_build`: A large proportion of the code in this crate is only used for testing. For a
28//!   typical user, building this code would result in an unnecessarily long compilation time and
29//!   an unnecessarily large binary. My solution is to only build this code when the `test_build`
30//!   feature is enabled. If you want to run unit tests, you must enable `test_build`. However,
31//!   doctests don't require it, since they only test the public interface.
32//! - `bin_build`: This feature is used to build the code for demos and benchmarks, which also
33//!   takes a long time to build. Enabling this feature also enables `test_build`.
34
35#![forbid(unsafe_code)]
36#![allow(
37    unstable_name_collisions,
38    clippy::assertions_on_constants,
39    clippy::cognitive_complexity,
40    clippy::many_single_char_names,
41    clippy::range_plus_one,
42    clippy::suspicious_arithmetic_impl,
43    clippy::suspicious_op_assign_impl,
44    clippy::too_many_arguments,
45    clippy::type_complexity,
46    clippy::upper_case_acronyms,
47    clippy::multiple_bound_locations
48)]
49#![warn(
50    clippy::cast_lossless,
51    clippy::comparison_chain,
52    clippy::explicit_into_iter_loop,
53    clippy::explicit_iter_loop,
54    clippy::filter_map_next,
55    clippy::large_digit_groups,
56    clippy::manual_filter_map,
57    clippy::manual_find_map,
58    clippy::map_flatten,
59    clippy::map_unwrap_or,
60    clippy::match_same_arms,
61    clippy::missing_const_for_fn,
62    clippy::mut_mut,
63    clippy::needless_borrow,
64    clippy::needless_continue,
65    clippy::needless_pass_by_value,
66    clippy::print_stdout,
67    clippy::redundant_closure_for_method_calls,
68    clippy::single_match_else,
69    clippy::trait_duplication_in_bounds,
70    clippy::type_repetition_in_bounds,
71    clippy::uninlined_format_args,
72    clippy::unused_self,
73    clippy::if_not_else,
74    clippy::manual_assert,
75    clippy::range_plus_one,
76    clippy::redundant_else,
77    clippy::semicolon_if_nothing_returned,
78    clippy::cloned_instead_of_copied,
79    clippy::flat_map_option,
80    clippy::unnecessary_wraps,
81    clippy::unnested_or_patterns,
82    clippy::use_self,
83    clippy::trivially_copy_pass_by_ref
84)]
85#![cfg_attr(
86    not(any(feature = "test_build", feature = "random", feature = "std")),
87    no_std
88)]
89
90extern crate alloc;
91
92#[macro_use]
93extern crate malachite_base;
94
95#[cfg(feature = "serde")]
96#[macro_use]
97extern crate serde;
98
99#[cfg(feature = "test_build")]
100extern crate itertools;
101
102use core::cmp::Ordering::{self, *};
103use malachite_base::num::arithmetic::traits::IsPowerOf2;
104use malachite_base::num::basic::floats::PrimitiveFloat;
105use malachite_base::num::basic::traits::{Infinity, NegativeInfinity};
106use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom, SciMantissaAndExponent};
107use malachite_base::rounding_modes::RoundingMode::*;
108use malachite_q::Rational;
109
110#[allow(clippy::type_repetition_in_bounds)]
111#[doc(hidden)]
112pub fn emulate_float_to_float_fn<T: PrimitiveFloat, F: Fn(Float, u64) -> (Float, Ordering)>(
113    f: F,
114    x: T,
115) -> T
116where
117    Float: From<T> + PartialOrd<T>,
118    for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
119{
120    let x = Float::from(x);
121    let (mut result, o) = f(x.clone(), T::MANTISSA_WIDTH + 1);
122    if !result.is_normal() {
123        return T::exact_from(&result);
124    }
125    let e = i64::from(<&Float as SciMantissaAndExponent<Float, i32, _>>::sci_exponent(&result));
126    if e < T::MIN_NORMAL_EXPONENT {
127        if e < T::MIN_EXPONENT {
128            let rm =
129                if e == T::MIN_EXPONENT - 1 && result.significand_ref().unwrap().is_power_of_2() {
130                    let down = if result > T::ZERO { Less } else { Greater };
131                    if o == down { Up } else { Down }
132                } else {
133                    Nearest
134                };
135            return T::rounding_from(&result, rm).0;
136        }
137        result = f(x, T::max_precision_for_sci_exponent(e)).0;
138    }
139    if result > T::MAX_FINITE {
140        T::INFINITY
141    } else if result < -T::MAX_FINITE {
142        T::NEGATIVE_INFINITY
143    } else {
144        T::exact_from(&result)
145    }
146}
147
148#[allow(clippy::type_repetition_in_bounds)]
149#[doc(hidden)]
150pub fn emulate_float_float_to_float_fn<
151    T: PrimitiveFloat,
152    F: Fn(Float, Float, u64) -> (Float, Ordering),
153>(
154    f: F,
155    x: T,
156    y: T,
157) -> T
158where
159    Float: From<T> + PartialOrd<T>,
160    for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
161{
162    let x = Float::from(x);
163    let y = Float::from(y);
164    let (mut result, o) = f(x.clone(), y.clone(), T::MANTISSA_WIDTH + 1);
165    if !result.is_normal() {
166        return T::exact_from(&result);
167    }
168    let e = i64::from(<&Float as SciMantissaAndExponent<Float, i32, _>>::sci_exponent(&result));
169    if e < T::MIN_NORMAL_EXPONENT {
170        if e < T::MIN_EXPONENT {
171            let rm =
172                if e == T::MIN_EXPONENT - 1 && result.significand_ref().unwrap().is_power_of_2() {
173                    let down = if result > T::ZERO { Less } else { Greater };
174                    if o == down { Up } else { Down }
175                } else {
176                    Nearest
177                };
178            return T::rounding_from(&result, rm).0;
179        }
180        result = f(x, y, T::max_precision_for_sci_exponent(e)).0;
181    }
182    if result > T::MAX_FINITE {
183        T::INFINITY
184    } else if result < -T::MAX_FINITE {
185        T::NEGATIVE_INFINITY
186    } else {
187        T::exact_from(&result)
188    }
189}
190
191#[allow(clippy::type_repetition_in_bounds)]
192#[doc(hidden)]
193pub fn emulate_rational_to_float_fn<T: PrimitiveFloat, F: Fn(&Rational, u64) -> (Float, Ordering)>(
194    f: F,
195    x: &Rational,
196) -> T
197where
198    Float: PartialOrd<T>,
199    for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
200{
201    let (mut result, o) = f(x, T::MANTISSA_WIDTH + 1);
202    if !result.is_normal() {
203        return T::exact_from(&result);
204    }
205    let e = i64::from(<&Float as SciMantissaAndExponent<Float, i32, _>>::sci_exponent(&result));
206    if e < T::MIN_NORMAL_EXPONENT {
207        if e < T::MIN_EXPONENT {
208            let rm =
209                if e == T::MIN_EXPONENT - 1 && result.significand_ref().unwrap().is_power_of_2() {
210                    let down = if result > T::ZERO { Less } else { Greater };
211                    if o == down { Up } else { Down }
212                } else {
213                    Nearest
214                };
215            return T::rounding_from(&result, rm).0;
216        }
217        result = f(x, T::max_precision_for_sci_exponent(e)).0;
218    }
219    if result > T::MAX_FINITE {
220        T::INFINITY
221    } else if result < -T::MAX_FINITE {
222        T::NEGATIVE_INFINITY
223    } else {
224        T::exact_from(&result)
225    }
226}
227
228#[allow(clippy::type_repetition_in_bounds)]
229#[doc(hidden)]
230pub fn emulate_rational_rational_to_float_fn<
231    T: PrimitiveFloat,
232    F: Fn(&Rational, &Rational, u64) -> (Float, Ordering),
233>(
234    f: F,
235    x: &Rational,
236    y: &Rational,
237) -> T
238where
239    Float: PartialOrd<T>,
240    for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
241{
242    let (mut result, o) = f(x, y, T::MANTISSA_WIDTH + 1);
243    if !result.is_normal() {
244        return T::exact_from(&result);
245    }
246    let e = i64::from(<&Float as SciMantissaAndExponent<Float, i32, _>>::sci_exponent(&result));
247    if e < T::MIN_NORMAL_EXPONENT {
248        if e < T::MIN_EXPONENT {
249            let rm =
250                if e == T::MIN_EXPONENT - 1 && result.significand_ref().unwrap().is_power_of_2() {
251                    let down = if result > T::ZERO { Less } else { Greater };
252                    if o == down { Up } else { Down }
253                } else {
254                    Nearest
255                };
256            return T::rounding_from(&result, rm).0;
257        }
258        result = f(x, y, T::max_precision_for_sci_exponent(e)).0;
259    }
260    if result > T::MAX_FINITE {
261        T::INFINITY
262    } else if result < -T::MAX_FINITE {
263        T::NEGATIVE_INFINITY
264    } else {
265        T::exact_from(&result)
266    }
267}
268
269/// Given the `(Float, Ordering)` result of an operation, determines whether an overflow occurred.
270///
271/// We're defining an overflow to occur whenever the actual result is outside the representable
272/// finite range, and is rounded to either infinity or to the maximum or minimum representable
273/// finite value. An overflow can present itself in four ways:
274/// - The result is $\infty$ and the `Ordering` is `Greater`
275/// - The result is $-\infty$ and the `Ordering` is `Less`
276/// - The result is the largest finite value (of any `Float` with its precision) and the `Ordering`
277///   is `Less`
278/// - The result is the smallest (most negative) finite value (of any `Float` with its precision)
279///   and the `Ordering` is `Greater`
280///
281/// # Worst-case complexity
282/// $T(n) = O(n)$
283///
284/// $M(n) = O(1)$
285///
286/// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
287///
288/// # Examples
289/// ```
290/// use malachite_base::num::basic::traits::{Infinity, NegativeInfinity, One};
291/// use malachite_float::{test_overflow, Float};
292/// use std::cmp::Ordering::*;
293///
294/// assert!(test_overflow(&Float::INFINITY, Greater));
295/// assert!(test_overflow(&Float::NEGATIVE_INFINITY, Less));
296/// assert!(test_overflow(&Float::max_finite_value_with_prec(10), Less));
297/// assert!(test_overflow(
298///     &-Float::max_finite_value_with_prec(10),
299///     Greater
300/// ));
301///
302/// assert!(!test_overflow(&Float::INFINITY, Equal));
303/// assert!(!test_overflow(&Float::ONE, Less));
304/// ```
305pub fn test_overflow(result: &Float, o: Ordering) -> bool {
306    if o == Equal {
307        return false;
308    }
309    *result == Float::INFINITY && o == Greater
310        || *result == Float::NEGATIVE_INFINITY && o == Less
311        || *result > 0u32 && result.abs_is_max_finite_value_with_prec() && o == Less
312        || *result < 0u32 && result.abs_is_max_finite_value_with_prec() && o == Greater
313}
314
315/// Given the `(Float, Ordering)` result of an operation, determines whether an underflow occurred.
316///
317/// We're defining an underflow to occur whenever the actual result is outside the representable
318/// finite range, and is rounded to zero, to the minimum positive value, or to the maximum negative
319/// value. An underflow can present itself in four ways:
320/// - The result is $0.0$ or $-0.0$ and the `Ordering` is `Less`
321/// - The result is $0.0$ or $-0.0$ and the `Ordering` is `Greater`
322/// - The result is the smallest positive value and the `Ordering` is `Greater`
323/// - The result is the largest (least negative) negative value and the `Ordering` is `Less`
324///
325/// # Worst-case complexity
326/// $T(n) = O(n)$
327///
328/// $M(n) = O(1)$
329///
330/// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
331///
332/// # Examples
333/// ```
334/// use malachite_base::num::basic::traits::{One, Zero};
335/// use malachite_float::{test_underflow, Float};
336/// use std::cmp::Ordering::*;
337///
338/// assert!(test_underflow(&Float::ZERO, Less));
339/// assert!(test_underflow(&Float::ZERO, Greater));
340/// assert!(test_underflow(&Float::min_positive_value_prec(10), Greater));
341/// assert!(test_underflow(&-Float::min_positive_value_prec(10), Less));
342///
343/// assert!(!test_underflow(&Float::ZERO, Equal));
344/// assert!(!test_underflow(&Float::ONE, Less));
345/// ```
346pub fn test_underflow(result: &Float, o: Ordering) -> bool {
347    if o == Equal {
348        return false;
349    }
350    *result == 0u32
351        || *result > 0u32 && result.abs_is_min_positive_value() && o == Greater
352        || *result < 0u32 && result.abs_is_min_positive_value() && o == Less
353}
354
355/// [`Float`], the crate's floating-point type, and everything defined on it.
356#[macro_use]
357pub mod float;
358pub use float::{ComparableFloat, ComparableFloatRef, Float};
359pub(crate) use float::{
360    InnerFloat, TWICE_WIDTH, WIDTH_MINUS_1, floor_and_ceiling, significand_bits,
361};
362
363#[cfg(feature = "test_build")]
364pub mod test_util;