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//! # Complexity conventions
15//! Functions in this crate are documented with worst-case time and additional-memory bounds,
16//! following the conventions described in the `malachite-base`
17//! [docs](https://docs.rs/malachite-base/latest/malachite_base/#complexity-conventions).
18//!
19//! # Demos and benchmarks
20//! This crate comes with a `bin` target that can be used for running demos and benchmarks.
21//! - Almost all of the public functions in this crate have an associated demo. Running a demo
22//!   shows you a function's behavior on a large number of inputs. For example, to demo [`Float`]
23//!   addition, you can use the following command:
24//!   ```text
25//!   cargo run --features bin_build --release -- -l 10000 -m exhaustive -d demo_float_add
26//!   ```
27//!   This command uses the `exhaustive` mode, which generates every possible input, generally
28//!   starting with the simplest input and progressing to more complex ones. Another mode is
29//!   `random`. The `-l` flag specifies how many inputs should be generated.
30//! - You can use a similar command to run benchmarks. The following command benchmarks various
31//!   [`Float`] addition implementations:
32//!   ```text
33//!   cargo run --features bin_build --release -- -l 1000000 -m random -b \
34//!       benchmark_float_add_algorithms -o add-bench.gp
35//!   ```
36//!   This creates a file called add-bench.gp. You can use gnuplot to create an SVG from it like
37//!   so:
38//!   ```text
39//!   gnuplot -e "set terminal svg; l \"add-bench.gp\"" > add-bench.svg
40//!   ```
41//!
42//! The list of available demos and benchmarks is not documented anywhere; you must find them by
43//! browsing through
44//! [`bin_util/demo_and_bench`](https://github.com/mhogrefe/malachite/tree/master/malachite-float/src/bin_util/demo_and_bench).
45//!
46//! # Features
47//! - `32_bit_limbs`: Sets the type of [`Limb`](malachite_nz#limbs) to [`u32`] instead of the
48//!   default, [`u64`].
49//! - `test_build`: A large proportion of the code in this crate is only used for testing. For a
50//!   typical user, building this code would result in an unnecessarily long compilation time and
51//!   an unnecessarily large binary. My solution is to only build this code when the `test_build`
52//!   feature is enabled. If you want to run unit tests, you must enable `test_build`. However,
53//!   doctests don't require it, since they only test the public interface.
54//! - `bin_build`: This feature is used to build the code for demos and benchmarks, which also
55//!   takes a long time to build. Enabling this feature also enables `test_build`.
56
57#![forbid(unsafe_code)]
58#![allow(
59    unstable_name_collisions,
60    clippy::assertions_on_constants,
61    clippy::cognitive_complexity,
62    clippy::many_single_char_names,
63    clippy::range_plus_one,
64    clippy::suspicious_arithmetic_impl,
65    clippy::suspicious_op_assign_impl,
66    clippy::too_many_arguments,
67    clippy::type_complexity,
68    clippy::upper_case_acronyms,
69    clippy::multiple_bound_locations
70)]
71#![warn(
72    clippy::cast_lossless,
73    clippy::comparison_chain,
74    clippy::explicit_into_iter_loop,
75    clippy::explicit_iter_loop,
76    clippy::filter_map_next,
77    clippy::large_digit_groups,
78    clippy::manual_filter_map,
79    clippy::manual_find_map,
80    clippy::map_flatten,
81    clippy::map_unwrap_or,
82    clippy::match_same_arms,
83    clippy::missing_const_for_fn,
84    clippy::mut_mut,
85    clippy::needless_borrow,
86    clippy::needless_continue,
87    clippy::needless_pass_by_value,
88    clippy::print_stdout,
89    clippy::redundant_closure_for_method_calls,
90    clippy::single_match_else,
91    clippy::trait_duplication_in_bounds,
92    clippy::type_repetition_in_bounds,
93    clippy::uninlined_format_args,
94    clippy::unused_self,
95    clippy::if_not_else,
96    clippy::manual_assert,
97    clippy::range_plus_one,
98    clippy::redundant_else,
99    clippy::semicolon_if_nothing_returned,
100    clippy::cloned_instead_of_copied,
101    clippy::flat_map_option,
102    clippy::unnecessary_wraps,
103    clippy::unnested_or_patterns,
104    clippy::use_self,
105    clippy::trivially_copy_pass_by_ref
106)]
107#![cfg_attr(
108    not(any(feature = "test_build", feature = "random", feature = "std")),
109    no_std
110)]
111
112extern crate alloc;
113
114#[macro_use]
115extern crate malachite_base;
116
117#[cfg(feature = "serde")]
118#[macro_use]
119extern crate serde;
120#[macro_use]
121mod macros;
122
123#[cfg(feature = "test_build")]
124extern crate itertools;
125
126use core::cmp::Ordering::{self, *};
127use malachite_base::num::basic::floats::PrimitiveFloat;
128use malachite_base::num::basic::traits::{Infinity, NegativeInfinity};
129use malachite_base::num::conversion::traits::ExactFrom;
130use malachite_base::rounding_modes::RoundingMode::*;
131use malachite_q::Rational;
132
133#[allow(clippy::type_repetition_in_bounds)]
134fn emulate_finish<T: PrimitiveFloat>(mut result: Float, o: Ordering) -> T
135where
136    Float: PartialOrd<T>,
137    for<'a> T: ExactFrom<&'a Float>,
138{
139    if !result.is_normal() {
140        return T::exact_from(&result);
141    }
142    // The result was correctly rounded to MANTISSA_WIDTH + 1 bits with the ternary o; gradual
143    // underflow is then emulated with a subnormalize pass, as in MPFR's recipe for emulating IEEE
144    // arithmetic. The minimum normal exponent is converted from the scientific convention to the
145    // raw convention.
146    result.subnormalize_assign(o, T::MIN_NORMAL_EXPONENT + 1, Nearest);
147    if result > T::MAX_FINITE {
148        T::INFINITY
149    } else if result < -T::MAX_FINITE {
150        T::NEGATIVE_INFINITY
151    } else {
152        T::exact_from(&result)
153    }
154}
155
156#[allow(clippy::type_repetition_in_bounds)]
157#[doc(hidden)]
158pub fn emulate_float_to_float_fn<T: PrimitiveFloat, F: Fn(Float, u64) -> (Float, Ordering)>(
159    f: F,
160    x: T,
161) -> T
162where
163    Float: From<T> + PartialOrd<T>,
164    for<'a> T: ExactFrom<&'a Float>,
165{
166    let x = Float::from(x);
167    let (result, o) = f(x, T::MANTISSA_WIDTH + 1);
168    emulate_finish(result, o)
169}
170
171#[allow(clippy::type_repetition_in_bounds)]
172#[doc(hidden)]
173pub fn emulate_float_to_float_pair_fn<
174    T: PrimitiveFloat,
175    F: Fn(Float, u64) -> (Float, Float, Ordering, Ordering),
176>(
177    f: F,
178    x: T,
179) -> (T, T)
180where
181    Float: From<T> + PartialOrd<T>,
182    for<'a> T: ExactFrom<&'a Float>,
183{
184    let x = Float::from(x);
185    let (a, b, o_a, o_b) = f(x, T::MANTISSA_WIDTH + 1);
186    (emulate_finish(a, o_a), emulate_finish(b, o_b))
187}
188
189#[allow(clippy::type_repetition_in_bounds)]
190#[doc(hidden)]
191pub fn emulate_rational_to_float_pair_fn<
192    T: PrimitiveFloat,
193    F: Fn(&Rational, u64) -> (Float, Float, Ordering, Ordering),
194>(
195    f: F,
196    x: &Rational,
197) -> (T, T)
198where
199    Float: PartialOrd<T>,
200    for<'a> T: ExactFrom<&'a Float>,
201{
202    let (a, b, o_a, o_b) = f(x, T::MANTISSA_WIDTH + 1);
203    (emulate_finish(a, o_a), emulate_finish(b, o_b))
204}
205
206#[allow(clippy::type_repetition_in_bounds)]
207#[doc(hidden)]
208pub fn emulate_constant_to_float_fn<T: PrimitiveFloat, F: Fn(u64) -> (Float, Ordering)>(f: F) -> T
209where
210    Float: PartialOrd<T>,
211    for<'a> T: ExactFrom<&'a Float>,
212{
213    let (result, o) = f(T::MANTISSA_WIDTH + 1);
214    emulate_finish(result, o)
215}
216
217#[allow(clippy::type_repetition_in_bounds)]
218#[doc(hidden)]
219pub fn emulate_float_float_to_float_fn<
220    T: PrimitiveFloat,
221    F: Fn(Float, Float, u64) -> (Float, Ordering),
222>(
223    f: F,
224    x: T,
225    y: T,
226) -> T
227where
228    Float: From<T> + PartialOrd<T>,
229    for<'a> T: ExactFrom<&'a Float>,
230{
231    let x = Float::from(x);
232    let y = Float::from(y);
233    let (result, o) = f(x, y, T::MANTISSA_WIDTH + 1);
234    emulate_finish(result, o)
235}
236
237#[allow(clippy::type_repetition_in_bounds)]
238#[doc(hidden)]
239pub fn emulate_float_float_float_to_float_fn<
240    T: PrimitiveFloat,
241    F: Fn(Float, Float, Float, u64) -> (Float, Ordering),
242>(
243    f: F,
244    x: T,
245    y: T,
246    z: T,
247) -> T
248where
249    Float: From<T> + PartialOrd<T>,
250    for<'a> T: ExactFrom<&'a Float>,
251{
252    let x = Float::from(x);
253    let y = Float::from(y);
254    let z = Float::from(z);
255    let (result, o) = f(x, y, z, T::MANTISSA_WIDTH + 1);
256    emulate_finish(result, o)
257}
258
259#[allow(clippy::type_repetition_in_bounds)]
260#[doc(hidden)]
261pub fn emulate_float_float_float_float_to_float_fn<
262    T: PrimitiveFloat,
263    F: Fn(Float, Float, Float, Float, u64) -> (Float, Ordering),
264>(
265    f: F,
266    x: T,
267    y: T,
268    z: T,
269    w: T,
270) -> T
271where
272    Float: From<T> + PartialOrd<T>,
273    for<'a> T: ExactFrom<&'a Float>,
274{
275    let x = Float::from(x);
276    let y = Float::from(y);
277    let z = Float::from(z);
278    let w = Float::from(w);
279    let (result, o) = f(x, y, z, w, T::MANTISSA_WIDTH + 1);
280    emulate_finish(result, o)
281}
282
283#[allow(clippy::type_repetition_in_bounds)]
284#[doc(hidden)]
285pub fn emulate_float_slice_to_float_fn<
286    T: PrimitiveFloat,
287    F: Fn(&[Float], u64) -> (Float, Ordering),
288>(
289    f: F,
290    xs: &[T],
291) -> T
292where
293    Float: From<T> + PartialOrd<T>,
294    for<'a> T: ExactFrom<&'a Float>,
295{
296    let xs: alloc::vec::Vec<Float> = xs.iter().map(|&x| Float::from(x)).collect();
297    let (result, o) = f(&xs, T::MANTISSA_WIDTH + 1);
298    emulate_finish(result, o)
299}
300
301#[allow(clippy::type_repetition_in_bounds)]
302#[doc(hidden)]
303pub fn emulate_float_slice_float_slice_to_float_fn<
304    T: PrimitiveFloat,
305    F: Fn(&[Float], &[Float], u64) -> (Float, Ordering),
306>(
307    f: F,
308    xs: &[T],
309    ys: &[T],
310) -> T
311where
312    Float: From<T> + PartialOrd<T>,
313    for<'a> T: ExactFrom<&'a Float>,
314{
315    let xs: alloc::vec::Vec<Float> = xs.iter().map(|&x| Float::from(x)).collect();
316    let ys: alloc::vec::Vec<Float> = ys.iter().map(|&y| Float::from(y)).collect();
317    let (result, o) = f(&xs, &ys, T::MANTISSA_WIDTH + 1);
318    emulate_finish(result, o)
319}
320
321#[allow(clippy::type_repetition_in_bounds)]
322#[doc(hidden)]
323pub fn emulate_float_to_float_and_i64_fn<
324    T: PrimitiveFloat,
325    F: Fn(Float, u64) -> (Float, Ordering, i64),
326>(
327    f: F,
328    x: T,
329) -> (T, i64)
330where
331    Float: From<T> + PartialOrd<T>,
332    for<'a> T: ExactFrom<&'a Float>,
333{
334    let x = Float::from(x);
335    let (result, o, quo) = f(x, T::MANTISSA_WIDTH + 1);
336    // the quotient bits are computed from the exact values, so they are unaffected by the
337    // subnormalization of the remainder
338    (emulate_finish(result, o), quo)
339}
340
341#[allow(clippy::type_repetition_in_bounds)]
342#[doc(hidden)]
343pub fn emulate_float_float_to_float_and_i64_fn<
344    T: PrimitiveFloat,
345    F: Fn(Float, Float, u64) -> (Float, Ordering, i64),
346>(
347    f: F,
348    x: T,
349    y: T,
350) -> (T, i64)
351where
352    Float: From<T> + PartialOrd<T>,
353    for<'a> T: ExactFrom<&'a Float>,
354{
355    let x = Float::from(x);
356    let y = Float::from(y);
357    let (result, o, quo) = f(x, y, T::MANTISSA_WIDTH + 1);
358    // the quotient bits are computed from the exact values, so they are unaffected by the
359    // subnormalization of the remainder
360    (emulate_finish(result, o), quo)
361}
362
363#[allow(clippy::type_repetition_in_bounds)]
364#[doc(hidden)]
365pub fn emulate_rational_to_float_fn<T: PrimitiveFloat, F: Fn(&Rational, u64) -> (Float, Ordering)>(
366    f: F,
367    x: &Rational,
368) -> T
369where
370    Float: PartialOrd<T>,
371    for<'a> T: ExactFrom<&'a Float>,
372{
373    let (result, o) = f(x, T::MANTISSA_WIDTH + 1);
374    emulate_finish(result, o)
375}
376
377#[allow(clippy::type_repetition_in_bounds)]
378#[doc(hidden)]
379pub fn emulate_rational_rational_to_float_fn<
380    T: PrimitiveFloat,
381    F: Fn(&Rational, &Rational, u64) -> (Float, Ordering),
382>(
383    f: F,
384    x: &Rational,
385    y: &Rational,
386) -> T
387where
388    Float: PartialOrd<T>,
389    for<'a> T: ExactFrom<&'a Float>,
390{
391    let (result, o) = f(x, y, T::MANTISSA_WIDTH + 1);
392    emulate_finish(result, o)
393}
394
395/// Given the `(Float, Ordering)` result of an operation, determines whether an overflow occurred.
396///
397/// We're defining an overflow to occur whenever the actual result is outside the representable
398/// finite range, and is rounded to either infinity or to the maximum or minimum representable
399/// finite value. An overflow can present itself in four ways:
400/// - The result is $\infty$ and the `Ordering` is `Greater`
401/// - The result is $-\infty$ and the `Ordering` is `Less`
402/// - The result is the largest finite value (of any `Float` with its precision) and the `Ordering`
403///   is `Less`
404/// - The result is the smallest (most negative) finite value (of any `Float` with its precision)
405///   and the `Ordering` is `Greater`
406///
407/// # Worst-case complexity
408/// $T(n) = O(n)$
409///
410/// $M(n) = O(1)$
411///
412/// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
413///
414/// # Examples
415/// ```
416/// use malachite_base::num::basic::traits::{Infinity, NegativeInfinity, One};
417/// use malachite_float::{Float, test_overflow};
418/// use std::cmp::Ordering::*;
419///
420/// assert!(test_overflow(&Float::INFINITY, Greater));
421/// assert!(test_overflow(&Float::NEGATIVE_INFINITY, Less));
422/// assert!(test_overflow(&Float::max_finite_value_with_prec(10), Less));
423/// assert!(test_overflow(
424///     &-Float::max_finite_value_with_prec(10),
425///     Greater
426/// ));
427///
428/// assert!(!test_overflow(&Float::INFINITY, Equal));
429/// assert!(!test_overflow(&Float::ONE, Less));
430/// ```
431pub fn test_overflow(result: &Float, o: Ordering) -> bool {
432    if o == Equal {
433        return false;
434    }
435    *result == Float::INFINITY && o == Greater
436        || *result == Float::NEGATIVE_INFINITY && o == Less
437        || *result > 0u32 && result.abs_is_max_finite_value_with_prec() && o == Less
438        || *result < 0u32 && result.abs_is_max_finite_value_with_prec() && o == Greater
439}
440
441/// Given the `(Float, Ordering)` result of an operation, determines whether an underflow occurred.
442///
443/// We're defining an underflow to occur whenever the actual result is outside the representable
444/// finite range, and is rounded to zero, to the minimum positive value, or to the maximum negative
445/// value. An underflow can present itself in four ways:
446/// - The result is $0.0$ or $-0.0$ and the `Ordering` is `Less`
447/// - The result is $0.0$ or $-0.0$ and the `Ordering` is `Greater`
448/// - The result is the smallest positive value and the `Ordering` is `Greater`
449/// - The result is the largest (least negative) negative value and the `Ordering` is `Less`
450///
451/// # Worst-case complexity
452/// $T(n) = O(n)$
453///
454/// $M(n) = O(1)$
455///
456/// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
457///
458/// # Examples
459/// ```
460/// use malachite_base::num::basic::traits::{One, Zero};
461/// use malachite_float::{Float, test_underflow};
462/// use std::cmp::Ordering::*;
463///
464/// assert!(test_underflow(&Float::ZERO, Less));
465/// assert!(test_underflow(&Float::ZERO, Greater));
466/// assert!(test_underflow(&Float::min_positive_value_prec(10), Greater));
467/// assert!(test_underflow(&-Float::min_positive_value_prec(10), Less));
468///
469/// assert!(!test_underflow(&Float::ZERO, Equal));
470/// assert!(!test_underflow(&Float::ONE, Less));
471/// ```
472pub fn test_underflow(result: &Float, o: Ordering) -> bool {
473    if o == Equal {
474        return false;
475    }
476    *result == 0u32
477        || *result > 0u32 && result.abs_is_min_positive_value() && o == Greater
478        || *result < 0u32 && result.abs_is_min_positive_value() && o == Less
479}
480
481/// [`Float`], the crate's floating-point type, and everything defined on it.
482#[macro_use]
483pub mod float;
484pub use float::{ComparableFloat, ComparableFloatRef, Float};
485pub(crate) use float::{
486    InnerFloat, TWICE_WIDTH, WIDTH_MINUS_1, floor_and_ceiling, significand_bits,
487};
488
489#[cfg(feature = "test_build")]
490pub mod test_util;