ordered-float

repository·master·Indexed 43 days ago

https://github.com/reem/rust-ordered-float

Rust wrappers for total ordering on floating-point numbers. Provides `OrderedFloat<T>` for types that implement `Ord` and `Eq` (treating NaN as greater than all other values) and `NotNan<T>` for floats guaranteed to never be NaN. Version 5.5.0 supports no_std environments and optional integrations with serde, rand, bytemuck, and others.

Tokens
3.5K
Snippets
14
Records
20
Agent score
6%

What's inside ordered-float

  1. What is ordered-float?

    master
    The ordered-float crate provides wrapper types for floating-point numbers (like f64 and others) that implement the Ord and Eq traits. This allows floating-point numbers to be used in contexts that require total ordering, such as being used as keys in a BTreeMap or elements in a BTreeSet, which standard floating-point types cannot do due to NaN values.
  2. Use ordered-float in no_std environments

    master

    To use ordered_float in environments without the Rust standard library, disable the default std feature in your Cargo.toml.

    If you are in a no_std environment but still need access to the Pow and Float traits, you must also enable the libm feature.

    [dependencies]
    ordered-float = { version = "5.0", default-features = false }
  3. Use OrderedFloat for total ordering of floats

    master

    The OrderedFloat<T> struct is a wrapper around floating-point types (like f32 or f64) that provides implementations for Eq, Ord, and Hash. This allows floats to be used in collections that require total ordering, such as HashSet, HashMap, BTreeMap, or BTreeSet.

    Ordering Behavior

    Unlike the standard IEEE floating-point behavior, OrderedFloat treats NaN as greater than all other values and equal to itself.

    Equality Nuances

    • -0.0 and +0.0 are considered equal.
    • All NaN values are considered equal, regardless of their underlying bit representation (sign, payload, etc.).

    Because of these equality rules, OrderedFloat might not be suitable for interning or memoization where bit-perfect identity is required, unless values are canonicalized before insertion.

    use ordered_float::OrderedFloat;
    use std::f32::NAN;
    
    // Sorting a list containing NaN
    let mut v = [OrderedFloat(NAN), OrderedFloat(2.0), OrderedFloat(1.0)];
    v.sort();
    assert_eq!(v, [OrderedFloat(1.0), OrderedFloat(2.0), OrderedFloat(NAN)]);
    
    // Using as a key in a HashSet
    let mut s = std::collections::HashSet::new();
    s.insert(OrderedFloat(NAN));
    assert!(s.contains(&OrderedFloat(NAN)));
  4. Use `NotNan<T>` to handle floats that cannot be NaN

    master

    NotNan<T> is a wrapper around floating-point types (like f32 or f64) that guarantees the value is never NaN. This allows the type to implement Eq, Ord, and Hash, making it suitable for use as keys in collections like HashSet, HashMap, BTreeMap, or BTreeSet.

    Key Behaviors

    • Equality: -0.0 and +0.0 are considered equal.
    • Arithmetic: Operations that would result in a NaN (e.g., infinity + -infinity) will panic.
    • Safety: It is #[repr(transparent)], so you can use transmute or pointer casts to convert between T and NotNan<T> as long as you do not create a NaN value.
    use ordered_float::NotNan;
    
    let mut v = [NotNan::new(2.0).unwrap(), NotNan::new(1.0).unwrap()];
    v.sort();
    assert_eq!(v, [1.0, 2.0]);
    
    // Use as a key in a HashSet
    use std::collections::HashSet;
    let mut s: HashSet<NotNan<f32>> = HashSet::new();
    let key = NotNan::new(1.0).unwrap();
    s.insert(key);
    assert!(s.contains(&key));
  5. Configure optional features for ordered-float

    master

    You can enable several optional features in Cargo.toml to add support for various serialization, testing, and mathematical crates:

    FeatureDescription
    libmEnables Pow and Float traits when std is disabled.
    arbitraryImplements arbitrary::Arbitrary trait.
    borshAdds borsh crate trait implementations.
    bytemuckAdds bytemuck crate trait implementations.
    derive-visitorAdds derive-visitor crate trait implementations.
    num-cmpImplements num_cmp::NumCmp trait.
    proptestImplements proptest::Arbitrary trait.
    randAdds implementations for rand crate distribution types.
    rkyv_16Implements rkyv traits with size_16.
    rkyv_32Implements rkyv traits with size_32.
    rkyv_64Implements rkyv traits with size_64.
    rkyv_ckImplements bytecheck::CheckBytes trait.
    schemarsImplements schemars::JsonSchema trait.
    serdeImplements serde::Serialize and serde::Deserialize traits.
    speedyImplements speedy's Readable and Writable traits.
  6. Perform arithmetic operations on OrderedFloat

    master

    OrderedFloat<T> supports standard arithmetic operations including Add, Sub, Mul, Div, Rem, and their assignment variants (AddAssign, etc.), as well as Pow (exponentiation). These operations are performed on the underlying float values, and the result is wrapped back into an OrderedFloat.

    use ordered_float::OrderedFloat;
    
    let a = OrderedFloat(10.0);
    let b = OrderedFloat(2.0);
    
    let sum = a + b;        // OrderedFloat(12.0)
    let diff = a - b;       // OrderedFloat(8.0)
    let prod = a * b;       // OrderedFloat(20.0)
    let quot = a / b;       // OrderedFloat(5.0)
    let power = a.pow(2.0); // OrderedFloat(100.0)
  7. Create a `NotNan<T>` value

    master

    Use the following methods to construct a NotNan instance:

    • NotNan::new(val: T) -> Result<Self, FloatIsNan>: Returns Err(FloatIsNan) if the value is NaN.
    • NotNan::new_unchecked(val: T) -> Self: Unsafe. Undefined behavior occurs if val is NaN. Use this when you have already verified the value is not NaN.
    • TryFrom<T> for NotNan<T>: Standard conversion that returns Err(FloatIsNan) if the input is NaN.
    use ordered_float::NotNan;
    
    // Safe construction
    let safe = NotNan::new(1.0).unwrap();
    
    // Unsafe construction (use with caution)
    let unsafe_val = unsafe { NotNan::new_unchecked(1.0) };
    
    // TryFrom
    let try_from = NotNan::try_from(1.0).unwrap();
  8. Generate JSON Schemas with Schemars

    master

    When the schemars feature is enabled, OrderedFloat<T> and NotNan<T> implement schemars::JsonSchema. The generated schemas represent these types as primitive numbers with appropriate formats:

    • OrderedFloat<f32> -> float
    • OrderedFloat<f64> -> double
    • NotNan<f32> -> float
    • NotNan<f64> -> double
  9. Extract the inner value from OrderedFloat

    master

    To retrieve the underlying floating-point value from an OrderedFloat<T>, use the into_inner() method. This consumes the wrapper and returns the inner type T.

    use ordered_float::OrderedFloat;
    
    let val = OrderedFloat(3.14);
    let inner: f64 = val.into_inner();
    assert_eq!(inner, 3.14);
  10. Use Bytemuck for zero-copy data handling

    master

    When the bytemuck feature is enabled, the types implement several traits for safe bit-level manipulation:

    • OrderedFloat<T>: Implements Zeroable, Pod, and TransparentWrapper<T> (if T is Pod).
    • NotNan<T>: Implements Zeroable, NoUninit, and CheckedBitPattern. It does not implement Pod because NaN bit patterns are considered invalid for this type.

    NotNan<T> can be used with bytemuck::checked::try_cast to safely convert from a primitive float, which will return Err(CheckedCastError::InvalidBitPattern) if the input is NaN.

    use bytemuck::checked::try_cast;
    
    let nan = f64::NAN;
    assert!(try_cast::<f64, NotNan<f64>>(nan).is_err());
    
    let pi = core::f64::consts::PI;
    assert!(try_cast::<f64, NotNan<f64>>(pi).is_ok());
  11. Generate random values with Rand

    master

    When the rand feature is enabled, OrderedFloat<T> and NotNan<T> implement rand::distributions::Distribution for standard distributions like Standard, Open01, and OpenClosed01.

    Additionally, specialized uniform samplers are provided:

    • UniformOrdered<T> for OrderedFloat<T>
    • UniformNotNan<T> for NotNan<T>
  12. Convert `NotNan<f64>` to `NotNan<f32>`

    master

    You can convert a NotNan<f64> to a NotNan<f32> using the as_f32() method. This involves a loss of precision and uses roundTiesToEven as the rounding mode. If the value overflows, it yields Infinity.

    use ordered_float::NotNan;
    
    let large = NotNan::new(1.0e30_f64).unwrap();
    let small = large.as_f32();