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
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...