range_set_blaze/float.rs
1//! Experimental support for floating point ranges.\
2//! Enable with `float_experimental` (stable, `f32`/`f64`) and
3//! `float_nightly_experimental` (nightly, adds `f16`/`f128`).
4//!
5//! Exports two types of floating point range types.\
6//! Total: Every bit pattern is valid and distinct.\
7//! Finite: Only finite floating point values are valid, e.g. `f64::MIN..=f64::MAX`. Also, -0.0 is treated as 0.0
8//!
9//! Each of those is available in four sizes: 16, 32, 64 and 128.
10//!
11//! ## Example: Merging Overlapping Intervals
12//!
13//! Turn a list of unsorted, overlapping f64 intervals (inclusive) into
14//! a sorted list of disjoint intervals (still inclusive).
15//! ```
16//! use range_set_blaze::{RangeSetBlaze, FiniteF64, finite::FiniteRangeExt};
17//!
18//! let overlapping_intervals = vec![
19//! (11.0, 13.0),
20//! (1.00000001, 2.99999999),
21//! (30.0, 31.0),
22//! (5.50000002, 9.00000006),
23//! (-4.0, 1.99999999),
24//! (15.0, 15.0),
25//! (7.00000003, 7.49999997),
26//! (21.00000004, 28.0),
27//! (2.50000005, 5.99999996),
28//! (19.0, 21.99999998),
29//! ];
30//!
31//! let disjoint_intervals: Vec<_> = overlapping_intervals
32//! .into_iter()
33//! .map(|(s, e)| FiniteF64::from_primitive_range(s..=e)) // Wrap each range
34//! .collect::<RangeSetBlaze<_>>() // Coalesce into disjoint ranges
35//! .ranges() // Convert back
36//! .map(FiniteRangeExt::into_primitive_inner)
37//! .collect();
38//!
39//! assert_eq!(
40//! disjoint_intervals,
41//! vec![
42//! (-4.0, 9.00000006),
43//! (11.0, 13.0),
44//! (15.0, 15.0),
45//! (19.0, 28.0),
46//! (30.0, 31.0)
47//! ]
48//! );
49//! ```
50//!
51//! ## Example: Wrapping Primitive (e.g. f32, f64) Ranges
52//!
53//! ```
54//! use range_set_blaze::{finite::ff64, total::{tf32, tf64}, RangeSetBlaze};
55//!
56//! // Alternatively, use `TotalF64::new`, `TotalF32::new`, and `FiniteF64::new`.
57//! let set = RangeSetBlaze::from_iter([tf64(3.0)..=tf64(5.0)]);
58//! assert!(set.contains(tf64(3.1)));
59//! assert!(!set.contains(tf64(2.9)));
60//!
61//! let set = RangeSetBlaze::from(ff64(3.0)..=ff64(5.0));
62//! assert!(set.contains(ff64(4.9)));
63//! assert!(!set.contains(ff64(5.1)));
64//!
65//! let set = RangeSetBlaze::from_iter([tf32(3.0)..=tf32(5.0), tf32(7.0)..=tf32(9.0)]);
66//! assert!(set.contains(tf32(4.0)));
67//! assert!(!set.contains(tf32(6.0)));
68//! ```
69//!
70//! ## Example: Coalescing Two Billion f32's
71#![doc = concat!(
72 "````rust,no_run\n",
73 include_str!("../examples/float_maps.rs"),
74 "\n````"
75)]
76//! ## Example: Total Floats
77//!
78//! Unlike `Finite`, `Total` uses `total_cmp` order, so it can also represent
79//! NaN, the infinities, and -0.0 as distinct, orderable values. This example
80//! builds a category map over all of `f64` from nothing but
81//! `TotalF64::MIN`/`MAX`, the two infinities, and `-0.0`: start with NaN
82//! everywhere, carve out the finite range between the infinities as Normal,
83//! then punch in the infinities and -0.0 as exceptions (overlapping ranges
84//! have right-to-left precedence, so later entries win).
85//!
86//! ```
87//! use range_set_blaze::{RangeMapBlaze, TotalF64, total::{tf64, TotalRangeExt}};
88//!
89//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
90//! enum Category {
91//! NaN,
92//! NegInfinity,
93//! PosInfinity,
94//! MinusZero,
95//! Normal,
96//! }
97//!
98//! let category_map = RangeMapBlaze::from_iter([
99//! (TotalF64::MIN..=TotalF64::MAX, Category::NaN),
100//! (tf64(f64::NEG_INFINITY)..=tf64(f64::INFINITY), Category::Normal),
101//! (tf64(f64::NEG_INFINITY)..=tf64(f64::NEG_INFINITY), Category::NegInfinity),
102//! (tf64(f64::INFINITY)..=tf64(f64::INFINITY), Category::PosInfinity),
103//! (tf64(-0.0)..=tf64(-0.0), Category::MinusZero),
104//! ]);
105//!
106//! # assert_eq!(category_map.get(tf64(f64::NAN)), Some(&Category::NaN));
107//! # assert_eq!(category_map.get(tf64(f64::NEG_INFINITY)), Some(&Category::NegInfinity));
108//! # assert_eq!(category_map.get(tf64(f64::INFINITY)), Some(&Category::PosInfinity));
109//! # assert_eq!(category_map.get(tf64(-0.0)), Some(&Category::MinusZero));
110//! # assert_eq!(category_map.get(tf64(0.0)), Some(&Category::Normal));
111//!
112//! for (range, category) in category_map.range_values() {
113//! let (start, end) = range.into_primitive_inner();
114//! println!(
115//! "{start:e} (0x{:016x}) ..= {end:e} (0x{:016x}) -> {category:?}",
116//! start.to_bits(),
117//! end.to_bits(),
118//! );
119//! }
120//! // Output:
121//! // NaN (0xffffffffffffffff) ..= NaN (0xfff0000000000001) -> NaN
122//! // -inf (0xfff0000000000000) ..= -inf (0xfff0000000000000) -> NegInfinity
123//! // -1.7976931348623157e308 (0xffefffffffffffff) ..= -5e-324 (0x8000000000000001) -> Normal
124//! // -0e0 (0x8000000000000000) ..= -0e0 (0x8000000000000000) -> MinusZero
125//! // 0e0 (0x0000000000000000) ..= 1.7976931348623157e308 (0x7fefffffffffffff) -> Normal
126//! // inf (0x7ff0000000000000) ..= inf (0x7ff0000000000000) -> PosInfinity
127//! // NaN (0x7ff0000000000001) ..= NaN (0x7fffffffffffffff) -> NaN
128//! ```
129
130mod finite_float;
131mod total_float;
132
133pub mod total;
134pub use total::{Total, TotalF32, TotalF64};
135#[cfg(feature = "float_nightly_experimental")]
136pub use total::{TotalF16, TotalF128};
137
138pub mod finite;
139pub use finite::{Finite, FiniteF32, FiniteF64};
140#[cfg(feature = "float_nightly_experimental")]
141pub use finite::{FiniteF16, FiniteF128};