floating point - How can I use a HashMap with f64 as key in Rust? -
i want use hashmap<f64, f64>
, saving distances of point known x , key y point. f64
value shouldn't matter here, focus should on key.
let mut map = hashmap<f64, f64>::new(); map.insert(0.4, f64::hypot(4.2, 50.0)); map.insert(1.8, f64::hypot(2.6, 50.0)); ... let = map.get(&0.4).unwrap();
as f64
neither eq
nor hash
, partialeq
, f64
not sufficient key. need save distances first, access distances later y. type of y needs floating point precision, if doesn't work f64
, i'll use i64
known exponent.
i tried hacks using own struct dimension(f64)
, implementing hash
converting float string
, hashing it.
#[derive(partialeq, eq)] struct dimensionkey(f64); impl hash dimensionkey { fn hash<h: hasher>(&self, state: &mut h) { format!("{}", self.0).hash(state); } }
it seems bad , both solutions, own struct or float integers base , exponent seem pretty complicated key.
update: can guarantee key never nan
, or infinite value. also, won't calculate keys, iterating on them , using them. there should no error known error 0.1 + 0.2 ≠ 0.3
. how binary search on vec of floats? , question have in common implement total ordering , equality floating number, difference lies in hashing or iterating.
you split f64
integral , fractional part , store them in struct in following manner:
#[derive(hash, eq, partialeq)] struct distance { integral: u64, fractional: u64 }
the rest straightforward:
use std::collections::hashmap; #[derive(hash, eq, partialeq)] struct distance { integral: u64, fractional: u64 } impl distance { fn new(i: u64, f: u64) -> distance { distance { integral: i, fractional: f } } } fn main() { let mut map: hashmap<distance, f64> = hashmap::new(); map.insert(distance::new(0, 4), f64::hypot(4.2, 50.0)); map.insert(distance::new(1, 8), f64::hypot(2.6, 50.0)); assert_eq!(map.get(&distance::new(0, 4)), some(&f64::hypot(4.2, 50.0))); }
edit: veedrac said, more general , efficient option deconstruct f64
mantissa-exponent-sign triplet. function can this, integer_decode()
, deprecated in std
, can found in rust github.
the integer_decode()
function can defined follows:
use std::mem; fn integer_decode(val: f64) -> (u64, i16, i8) { let bits: u64 = unsafe { mem::transmute(val) }; let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 }; let mut exponent: i16 = ((bits >> 52) & 0x7ff) i16; let mantissa = if exponent == 0 { (bits & 0xfffffffffffff) << 1 } else { (bits & 0xfffffffffffff) | 0x10000000000000 }; exponent -= 1023 + 52; (mantissa, exponent, sign) }
the definition of distance
be:
#[derive(hash, eq, partialeq)] struct distance((u64, i16, i8)); impl distance { fn new(val: f64) -> distance { distance(integer_decode(val)) } }
this variant easier use:
fn main() { let mut map: hashmap<distance, f64> = hashmap::new(); map.insert(distance::new(0.4), f64::hypot(4.2, 50.0)); map.insert(distance::new(1.8), f64::hypot(2.6, 50.0)); assert_eq!(map.get(&distance::new(0.4)), some(&f64::hypot(4.2, 50.0))); }
Comments
Post a Comment