Wrapper type arround float

Hi, back in a project I needed to build a heap that contains floats in Rust. I encountered a big problem because floats in Rust don't implement the Ord trait, so I can't use them in the std implementation.

I know that there are wrapper types in std like NonZeroI8. I checked through the Doc but didn't see any RealFloat32 or something like this. The main purpose of this would be that RealFloat32 is a simple wrapper type around f32, and to build it, the number only needs to not be +/- inf and NaN. This would allow us to implement the Ord trait, which would help to use floats in collections and allow putting semantics around validated floats in cases where those floats are not wanted.

So, I ask if there is a reason why such things don't exist in the Rust library (not a priority, not useful enough, better in a crate than std) or if it can be a good thing. Where can I go to make it be implemented in Rust std (even by myself) or if I can help? I think wrapping floats can be a good way to deal with floats and reduce runtime errors.

Thanks to all of the people who will read this and try to help!


NonZero* types are primarily defined in stdlib because of the compiler-internal niche value attribute. Such an attribute would not be applicable to a float.

Total ordering for f64 is implemented by the f64::total_cmp method (same for f32). Or you can use a crate that provides a float wrapper, like float-ord. Rust's standard library is small by design and has very strong compatibility guarantees. The threshold for adding new types is very high. That's why common numeric types like complex numbers or arbitrary sized integers aren't part of it but instead are implemented by the ecosystem.

2 Likes

The crate float-ord solve the Ord problem but the wrapper can wrap around Nan for example.

"NaN | -Infinity | x < 0 | -0 | +0 | x > 0 | +Infinity | NaN". So i don't ensure that Nan or infinity is not wrap in.So it do not add protection with those exception. But there is probably a crate to it in the way i want.

So by the rust design of the simplest std possible, the crate approach seems better because it keep std simple and give responsibility to the ecosystem. Ok i see Thanks for the awnser !

1 Like

typed_floats may be what you want. I haven't tried it, but it looks like a more modern alternative to noisy_float that I used long ago.

I test typed_float , I tried to make a min heap it looks a bit awkward
but it work !

type RevRealFloat = Reverse<NonNaNFinite<f64>>;

let array: [RevRealFloat; 10] = core::array::from_fn(|_| {
        Reverse(NonNaNFinite::<f64>::new(random::<f64>() * 100.0f64).unwrap())
    });
    let mut heap = BinaryHeap::from(array);
    for _ in 0..10 {
        println!("{} ", heap.pop().unwrap().0);
    }

output:
1.195202609390611 2.1727584408206213 8.05786831937445 9.736927975496734 19.82277188488144 51.83592511195274 66.13506911298586 75.68833797076135 81.47419629082951 93.5146559944814

I think it just need to implement my own MinHeap derive from std heap and it will be fine.
Thanks for the tip !

1 Like

A "NonNaN" float type would, however, both be Ord and have literally millions of niche values (well, 16.7 million for f32, ~1e16 for f64!)

2 Likes

Given the fact that Mozilla uses these niches to represent, literally, everything it's a bit strange that there are no crate that makes that work more ergonomic…

There are some. the technique is called NaN-boxing. Boxing

1 Like