zng_unit/float_eq.rs
1/// [`f32`] equality used in floating-point units.
2///
3/// * [`NaN`](f32::is_nan) values are equal.
4/// * [`INFINITY`](f32::INFINITY) values are equal.
5/// * [`NEG_INFINITY`](f32::NEG_INFINITY) values are equal.
6/// * Finite values are equal if they fall in the same *bucket* sized by `granularity`.
7///
8/// Note that this definition of equality is symmetric, reflexive, and transitive. This is slightly different them
9/// equality defined by minimal distance *epsilon*, in some cases `abs(a - b) < granularity` can be true and not equal because they
10/// are near a *bucket threshold*. In practice this does not mather given sufficient small `granularity`, it is more
11/// stable due to the transitive property and enables the [`about_eq_hash`] function to always output the same hash for the same values.
12pub fn about_eq(a: f32, b: f32, granularity: f32) -> bool {
13 f32_about_eq_snap(a, granularity) == f32_about_eq_snap(b, granularity)
14}
15
16/// [`f32`] hash compatible with [`about_eq`] equality.
17pub fn about_eq_hash<H: std::hash::Hasher>(f: f32, granularity: f32, state: &mut H) {
18 use std::hash::Hash;
19 f32_about_eq_snap(f, granularity).hash(state);
20}
21fn f32_about_eq_snap(f: f32, granularity: f32) -> (u8, i64) {
22 let (kind, bucket) = if f.is_nan() {
23 (255u8, 0i64)
24 } else if f.is_infinite() {
25 if f.is_sign_positive() { (254, 0) } else { (1, 0) }
26 } else {
27 let bucket = (f / granularity).floor() as i64;
28 (128, bucket)
29 };
30 (kind, bucket)
31}
32
33/// [`f32`] ordering compatible with [`about_eq`] equality.
34///
35/// The order is `-inf < finite < inf < NaN`.
36pub fn about_eq_ord(a: f32, b: f32, granularity: f32) -> std::cmp::Ordering {
37 let a = f32_about_eq_snap(a, granularity);
38 let b = f32_about_eq_snap(b, granularity);
39 a.cmp(&b)
40}
41
42/// Minimal bucket size for equality between values in around the 0.0..=1.0 scale.
43pub const EQ_GRANULARITY: f32 = 0.00001;
44/// Minimal bucket size for equality between values in around the 1.0..=100.0 scale.
45pub const EQ_GRANULARITY_100: f32 = 0.001;