Using entry() on f32

The project I'm curently working on (as part of my "learn Rust" activities) needs to use a vector full of floats to fill a hashmap, counting the occurrences of each number. Using the entry() method this worked just fine when I filled my vector with integers. However, to work properly I really need to use floats and when I try that, the compiler rejects my code with the message:

   |
29 |             .entry(fence[i])
   |              ^^^^^
   |
   = note: the following trait bounds were not satisfied:
           `f32: Eq`
           `f32: Hash`

Now I really don't yet have any understanding of why Eq and Hash are implemented over integers but not over floats, however, I'm sure there's a good reason. What I need is a workaround that will let me accomplish what I need to accomplish. Here's the code I'm using:

use std::collections::HashMap;

fn main() {
    let mut corn_keys: Vec<f32> = vec![
        2.0, 3.0, 4.0, 1.0, 2.0, 7.0, 8.0, 3.0, 4.0, 7.0, 2.0, 1.0, 5.0, 7.0, 2.0, 1.0, 9.0, 7.0, 1.0 ];

    println!("\nThe vector containing the keys is:   {:?}\n", corn_keys);

    post(&corn_keys);
}

fn post(fence: &Vec<f32>) {
    let mut feeder: HashMap<f32, f32> = HashMap::new();

    let mut i = 0;
    while i <= fence.len() - 1 {
        feeder
            .entry(fence[i])
            .and_modify(|counter| *counter += 1)
            .or_insert(1);
        i += 1;
    }   
}  

Note that the offending line of code in the while loop is copied directly from the example given in the Rust reference discussion for the entry() method.

Any thoughts on how I can get around this? Thanks.

Floating-point numbers can't be Eq (and Ord) because equality is not reflexive: NaN != NaN. They also can't be Hash because of the different bitwise representations of NaN, which would yield different hashes despite being "identical". All in all, floating-point numbers are really not well-behaved in many aspects.

If you want to put floating-point numbers in containers requiring equality/ordering/hashing, you can use wrapper types such as ordered_float::NotNan which ensure that the value is never NaN.

Also note that comparing floating-point numbers for exact equality is a Bad™ idea. See e.g. [1] for a good introduction to floating-point.

9 Likes

What is it exactly that you "need to accomplish"? It seems like you would be better served by a histogram or kernel density estimation of your input data or some other statistic.

I read through the links you included in your reply. They were very informative. Thanks. Floats are just funny critters and hard to work with. I get that. I'm still working on my problem and it has helped to get a better understanding of how floats work.

I'm just working on an exercise from the Rust Book that wants me to use a hashmap to find the mode of a set of numbers. I found a nice solution that works fine with integers but not with floats. I can think of lots of real-life scenarios where one would need to analyze a set of floats, so I made the problem more difficult by trying to find the mode of a set of floats. I've been a little vague about my goal because I'm not looking for someone to tell me the solution. (Where's the fun in that?) I just needed a better understanding of why my nice integer-oriented solution didn't work with floats. All in all it has been a really good learning activity to help me learn Rust. The reply from @H2CO3 above and the links he provided really helped.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.