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_constant_to_float_fn<T: PrimitiveFloat, F: Fn(u64) -> (Float, Ordering)>(f: F) -> T
174where
175    Float: PartialOrd<T>,
176    for<'a> T: ExactFrom<&'a Float>,
177{
178    let (result, o) = f(T::MANTISSA_WIDTH + 1);
179    emulate_finish(result, o)
180}
181
182#[allow(clippy::type_repetition_in_bounds)]
183#[doc(hidden)]
184pub fn emulate_float_float_to_float_fn<
185    T: PrimitiveFloat,
186    F: Fn(Float, Float, u64) -> (Float, Ordering),
187>(
188    f: F,
189    x: T,
190    y: T,
191) -> T
192where
193    Float: From<T> + PartialOrd<T>,
194    for<'a> T: ExactFrom<&'a Float>,
195{
196    let x = Float::from(x);
197    let y = Float::from(y);
198    let (result, o) = f(x, y, T::MANTISSA_WIDTH + 1);
199    emulate_finish(result, o)
200}
201
202#[allow(clippy::type_repetition_in_bounds)]
203#[doc(hidden)]
204pub fn emulate_float_float_float_to_float_fn<
205    T: PrimitiveFloat,
206    F: Fn(Float, Float, Float, u64) -> (Float, Ordering),
207>(
208    f: F,
209    x: T,
210    y: T,
211    z: T,
212) -> T
213where
214    Float: From<T> + PartialOrd<T>,
215    for<'a> T: ExactFrom<&'a Float>,
216{
217    let x = Float::from(x);
218    let y = Float::from(y);
219    let z = Float::from(z);
220    let (result, o) = f(x, y, z, T::MANTISSA_WIDTH + 1);
221    emulate_finish(result, o)
222}
223
224#[allow(clippy::type_repetition_in_bounds)]
225#[doc(hidden)]
226pub fn emulate_float_float_float_float_to_float_fn<
227    T: PrimitiveFloat,
228    F: Fn(Float, Float, Float, Float, u64) -> (Float, Ordering),
229>(
230    f: F,
231    x: T,
232    y: T,
233    z: T,
234    w: T,
235) -> T
236where
237    Float: From<T> + PartialOrd<T>,
238    for<'a> T: ExactFrom<&'a Float>,
239{
240    let x = Float::from(x);
241    let y = Float::from(y);
242    let z = Float::from(z);
243    let w = Float::from(w);
244    let (result, o) = f(x, y, z, w, T::MANTISSA_WIDTH + 1);
245    emulate_finish(result, o)
246}
247
248#[allow(clippy::type_repetition_in_bounds)]
249#[doc(hidden)]
250pub fn emulate_float_slice_to_float_fn<
251    T: PrimitiveFloat,
252    F: Fn(&[Float], u64) -> (Float, Ordering),
253>(
254    f: F,
255    xs: &[T],
256) -> T
257where
258    Float: From<T> + PartialOrd<T>,
259    for<'a> T: ExactFrom<&'a Float>,
260{
261    let xs: alloc::vec::Vec<Float> = xs.iter().map(|&x| Float::from(x)).collect();
262    let (result, o) = f(&xs, T::MANTISSA_WIDTH + 1);
263    emulate_finish(result, o)
264}
265
266#[allow(clippy::type_repetition_in_bounds)]
267#[doc(hidden)]
268pub fn emulate_float_slice_float_slice_to_float_fn<
269    T: PrimitiveFloat,
270    F: Fn(&[Float], &[Float], u64) -> (Float, Ordering),
271>(
272    f: F,
273    xs: &[T],
274    ys: &[T],
275) -> T
276where
277    Float: From<T> + PartialOrd<T>,
278    for<'a> T: ExactFrom<&'a Float>,
279{
280    let xs: alloc::vec::Vec<Float> = xs.iter().map(|&x| Float::from(x)).collect();
281    let ys: alloc::vec::Vec<Float> = ys.iter().map(|&y| Float::from(y)).collect();
282    let (result, o) = f(&xs, &ys, T::MANTISSA_WIDTH + 1);
283    emulate_finish(result, o)
284}
285
286#[allow(clippy::type_repetition_in_bounds)]
287#[doc(hidden)]
288pub fn emulate_float_to_float_and_i64_fn<
289    T: PrimitiveFloat,
290    F: Fn(Float, u64) -> (Float, Ordering, i64),
291>(
292    f: F,
293    x: T,
294) -> (T, i64)
295where
296    Float: From<T> + PartialOrd<T>,
297    for<'a> T: ExactFrom<&'a Float>,
298{
299    let x = Float::from(x);
300    let (result, o, quo) = f(x, T::MANTISSA_WIDTH + 1);
301    // the quotient bits are computed from the exact values, so they are unaffected by the
302    // subnormalization of the remainder
303    (emulate_finish(result, o), quo)
304}
305
306#[allow(clippy::type_repetition_in_bounds)]
307#[doc(hidden)]
308pub fn emulate_float_float_to_float_and_i64_fn<
309    T: PrimitiveFloat,
310    F: Fn(Float, Float, u64) -> (Float, Ordering, i64),
311>(
312    f: F,
313    x: T,
314    y: T,
315) -> (T, i64)
316where
317    Float: From<T> + PartialOrd<T>,
318    for<'a> T: ExactFrom<&'a Float>,
319{
320    let x = Float::from(x);
321    let y = Float::from(y);
322    let (result, o, quo) = f(x, y, T::MANTISSA_WIDTH + 1);
323    // the quotient bits are computed from the exact values, so they are unaffected by the
324    // subnormalization of the remainder
325    (emulate_finish(result, o), quo)
326}
327
328#[allow(clippy::type_repetition_in_bounds)]
329#[doc(hidden)]
330pub fn emulate_rational_to_float_fn<T: PrimitiveFloat, F: Fn(&Rational, u64) -> (Float, Ordering)>(
331    f: F,
332    x: &Rational,
333) -> T
334where
335    Float: PartialOrd<T>,
336    for<'a> T: ExactFrom<&'a Float>,
337{
338    let (result, o) = f(x, T::MANTISSA_WIDTH + 1);
339    emulate_finish(result, o)
340}
341
342#[allow(clippy::type_repetition_in_bounds)]
343#[doc(hidden)]
344pub fn emulate_rational_rational_to_float_fn<
345    T: PrimitiveFloat,
346    F: Fn(&Rational, &Rational, u64) -> (Float, Ordering),
347>(
348    f: F,
349    x: &Rational,
350    y: &Rational,
351) -> T
352where
353    Float: PartialOrd<T>,
354    for<'a> T: ExactFrom<&'a Float>,
355{
356    let (result, o) = f(x, y, T::MANTISSA_WIDTH + 1);
357    emulate_finish(result, o)
358}
359
360/// Given the `(Float, Ordering)` result of an operation, determines whether an overflow occurred.
361///
362/// We're defining an overflow to occur whenever the actual result is outside the representable
363/// finite range, and is rounded to either infinity or to the maximum or minimum representable
364/// finite value. An overflow can present itself in four ways:
365/// - The result is $\infty$ and the `Ordering` is `Greater`
366/// - The result is $-\infty$ and the `Ordering` is `Less`
367/// - The result is the largest finite value (of any `Float` with its precision) and the `Ordering`
368///   is `Less`
369/// - The result is the smallest (most negative) finite value (of any `Float` with its precision)
370///   and the `Ordering` is `Greater`
371///
372/// # Worst-case complexity
373/// $T(n) = O(n)$
374///
375/// $M(n) = O(1)$
376///
377/// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
378///
379/// # Examples
380/// ```
381/// use malachite_base::num::basic::traits::{Infinity, NegativeInfinity, One};
382/// use malachite_float::{test_overflow, Float};
383/// use std::cmp::Ordering::*;
384///
385/// assert!(test_overflow(&Float::INFINITY, Greater));
386/// assert!(test_overflow(&Float::NEGATIVE_INFINITY, Less));
387/// assert!(test_overflow(&Float::max_finite_value_with_prec(10), Less));
388/// assert!(test_overflow(
389///     &-Float::max_finite_value_with_prec(10),
390///     Greater
391/// ));
392///
393/// assert!(!test_overflow(&Float::INFINITY, Equal));
394/// assert!(!test_overflow(&Float::ONE, Less));
395/// ```
396pub fn test_overflow(result: &Float, o: Ordering) -> bool {
397    if o == Equal {
398        return false;
399    }
400    *result == Float::INFINITY && o == Greater
401        || *result == Float::NEGATIVE_INFINITY && o == Less
402        || *result > 0u32 && result.abs_is_max_finite_value_with_prec() && o == Less
403        || *result < 0u32 && result.abs_is_max_finite_value_with_prec() && o == Greater
404}
405
406/// Given the `(Float, Ordering)` result of an operation, determines whether an underflow occurred.
407///
408/// We're defining an underflow to occur whenever the actual result is outside the representable
409/// finite range, and is rounded to zero, to the minimum positive value, or to the maximum negative
410/// value. An underflow can present itself in four ways:
411/// - The result is $0.0$ or $-0.0$ and the `Ordering` is `Less`
412/// - The result is $0.0$ or $-0.0$ and the `Ordering` is `Greater`
413/// - The result is the smallest positive value and the `Ordering` is `Greater`
414/// - The result is the largest (least negative) negative value and the `Ordering` is `Less`
415///
416/// # Worst-case complexity
417/// $T(n) = O(n)$
418///
419/// $M(n) = O(1)$
420///
421/// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
422///
423/// # Examples
424/// ```
425/// use malachite_base::num::basic::traits::{One, Zero};
426/// use malachite_float::{test_underflow, Float};
427/// use std::cmp::Ordering::*;
428///
429/// assert!(test_underflow(&Float::ZERO, Less));
430/// assert!(test_underflow(&Float::ZERO, Greater));
431/// assert!(test_underflow(&Float::min_positive_value_prec(10), Greater));
432/// assert!(test_underflow(&-Float::min_positive_value_prec(10), Less));
433///
434/// assert!(!test_underflow(&Float::ZERO, Equal));
435/// assert!(!test_underflow(&Float::ONE, Less));
436/// ```
437pub fn test_underflow(result: &Float, o: Ordering) -> bool {
438    if o == Equal {
439        return false;
440    }
441    *result == 0u32
442        || *result > 0u32 && result.abs_is_min_positive_value() && o == Greater
443        || *result < 0u32 && result.abs_is_min_positive_value() && o == Less
444}
445
446/// [`Float`], the crate's floating-point type, and everything defined on it.
447#[macro_use]
448pub mod float;
449pub use float::{ComparableFloat, ComparableFloatRef, Float};
450pub(crate) use float::{
451    InnerFloat, TWICE_WIDTH, WIDTH_MINUS_1, floor_and_ceiling, significand_bits,
452};
453
454#[cfg(feature = "test_build")]
455pub mod test_util;