How can I use a HashMap with f64 as key in Rust?

前端 未结 3 398
刺人心
刺人心 2020-12-06 17:08

I want to use a HashMap, for saving the distances of a point with known x and key y to another point. f64 as value shouldn\'t matte

3条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-06 17:54

    Unfortunately, floating types equality is hard and counter-intuitive:

    fn main() {
        println!("{} {} {}", 0.1 + 0.2, 0.3, 0.1 + 0.2 == 0.3);
    }
    
    // Prints: 0.30000000000000004 0.3 false
    

    And therefore hashing is hard too, since hashes of equal values should be equal.


    If, in your case, you have a small enough range to fit your number in a i64 and you can accept the loss of precision, then a simple solution is to canonicalize first and then define equal/hash in terms of the canonical value:

    use std::cmp::Eq;
    
    #[derive(Debug)]
    struct Distance(f64);
    
    impl Distance {
        fn canonicalize(&self) -> i64 {
            (self.0 * 1024.0 * 1024.0).round() as i64
        }
    }
    
    impl PartialEq for Distance {
        fn eq(&self, other: &Distance) -> bool {
            self.canonicalize() == other.canonicalize()
        }
    }
    
    impl Eq for Distance {}
    
    fn main() {
        let d = Distance(0.1 + 0.2);
        let e = Distance(0.3);
    
        println!("{:?} {:?} {:?}", d, e, d == e);
    }
    
    // Prints: Distance(0.30000000000000004) Distance(0.3) true
    

    Hash just follows, and from then on you can use Distance as a key in the hash map:

    impl Hash for Distance {
        fn hash(&self, state: &mut H) where H: Hasher {
            self.canonicalize().hash(state);
        }
    }
    
    fn main() {
        let d = Distance(0.1 + 0.2);
        let e = Distance(0.3);
    
        let mut m = HashMap::new();
        m.insert(d, "Hello");
    
        println!("{:?}", m.get(&e));
    }
    
    // Prints: Some("Hello")
    

    Warning: To reiterate, this strategy only works if (a) the dynamic range of values is small enough to be captured in a i64 (19 digits) and if (b) the dynamic range is known in advance as the factor is static. Fortunately, this holds for many common problems, but it is something to document and test...

提交回复
热议问题