我如何使用f64的HashMap作为Rust中的键? [英] How can I use a HashMap with f64 as key in Rust?
问题描述
我想用 HashMap< f64,f64>
来保存已知x和关键点y到另一个点的距离。 f64
作为值不应该在这里,焦点应该在关键。
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 a = map.get(& 0.4).unwrap();
由于 f64
既不是 Eq
也不是 Hash
,但是只有 PartialEq
, f64
作为一个关键是不够的。我需要首先保存距离,但也需要稍后访问距离。 y的类型需要浮点精度,但是如果对 f64
不起作用,我将使用 i64
使用已知的指数。
我使用自己的 struct Dimension(f64)
尝试了一些黑客行为,然后通过将float转换为一个 String
来实现 Hash
,然后对其进行散列。
#[derive(PartialEq,Eq)]
struct DimensionKey(f64);
$ b $ impl Hash for DimensionKey {
fn hash< H:Hasher>(&self; state:&mut H){
format!({},self 0.0).hash(状态);
$ b看起来非常糟糕,或浮动为基数和指数的整数似乎是相当复杂的只是一个关键。
更新:
我可以保证我的关键永远不会是 NaN
,或者一个无限的值。另外,我不会计算我的密钥,只是迭代它们并使用它们。所以在 0.1 + 0.2≠0.3
的已知错误中应该没有错误。
如何进行二进制搜索在一个浮点数的Vec?,这个问题有一个共同的实现一个浮点数的总排序和相等性,区别只在于散列或迭代。
f64 拆分为整数和小数部分,并按照以下方式将它们存储在结构中: >
#[派生(Hash,Eq,PartialEq)]
结构距离{
积分:u64,
分数:u64
}
其余部分很简单:
使用std :: collections :: HashMap;
$ b#[derive(Hash,Eq,PartialEq)]
struct距离{
积分:u64,
分数:u64
}
impl距离{
fn new(i:u64,f:u64) - >距离{
距离{
积分:我,
分数: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)); (& LT;距离::新(0,4)),一些(& f64 :: hypot(4.2,50.0)));
assert_eq!(map.get
code
编辑:正如Veedrac所说,高效的选择是将 f64
解构为尾数 - 指数 - 符号三元组。可以这样做的函数, integer_decode( )
在 std
中不推荐使用,但可以在 Rust GitHub 。
integer_decode()
函数可以定义如下:
$ p $ 使用std :: mem;
fn integer_decode(val:f64) - > (u64,i16,i8){
let bits:u64 = unsafe {mem :: transmute(val)};
让sign:i8 = if bits>> 63 == 0 {1} else {-1};
let mut指数:i16 =((bits> gt; 52)& 0x7ff)为i16;
让尾数= if指数== 0 {
(bits& 0xfffffffffffff)<< 1
} else {
(bits& 0xfffffffffffff)| 0x10000000000000
};
指数 - = 1023 + 52;
(尾数,指数,符号)
}
定义距离
然后可以是:
#[derive(Hash,Eq,PartialEq) ]
struct距离((u64,i16,i8));
impl距离{
fn new(val:f64) - >距离(
距离(integer_decode(val))
}
}
<
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)); (& LT;距离::新(0.4)),一些(& f64 :: hypot(4.2,50.0)));
assert_eq!(map.get
}
I want to use a HashMap<f64, f64>
, for saving the distances of a point with known x and key y to another point. f64
as value shouldn't matter here, the focus should be 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 a = map.get(&0.4).unwrap();
As f64
is neither Eq
nor Hash
, but only PartialEq
, f64
is not sufficient as a key. I need to save the distances first, but also access the distances later by y. The type of y needs to be floating point precision, but if doesn't work with f64
, I'll use an i64
with an known exponent.
I tried some hacks by using my own struct Dimension(f64)
and then implementing Hash
by converting the float into a String
and then hashing it.
#[derive(PartialEq, Eq)]
struct DimensionKey(f64);
impl Hash for DimensionKey {
fn hash<H: Hasher>(&self, state: &mut H) {
format!("{}", self.0).hash(state);
}
}
It seems very bad and both solutions, my own struct or float as integers with base and exponent seem to be pretty complicated for just a key.
Update:
I can guarantee that my key never will be NaN
, or an infinite value. Also, I won't calculate my keys, only iterating over them and using them. So there should no error with the known error with 0.1 + 0.2 ≠ 0.3
.
How to do a binary search on a Vec of floats? and this question have in common to implement total ordering and equality for a floating number, the difference lies only in the hashing or iterating.
解决方案 You could split the f64
into the integral and fractional part and store them in a struct in the following manner:
#[derive(Hash, Eq, PartialEq)]
struct Distance {
integral: u64,
fractional: u64
}
The rest is 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: As Veedrac said, a more general and efficient option would be to deconstruct the f64
into a mantissa-exponent-sign triplet. The function that can do this, integer_decode()
, is deprecated in std
, but it can be easily found in Rust GitHub.
The integer_decode()
function can be defined as 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) as i16;
let mantissa = if exponent == 0 {
(bits & 0xfffffffffffff) << 1
} else {
(bits & 0xfffffffffffff) | 0x10000000000000
};
exponent -= 1023 + 52;
(mantissa, exponent, sign)
}
The definition of Distance
could then be:
#[derive(Hash, Eq, PartialEq)]
struct Distance((u64, i16, i8));
impl Distance {
fn new(val: f64) -> Distance {
Distance(integer_decode(val))
}
}
This variant is also easier to 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)));
}
这篇关于我如何使用f64的HashMap作为Rust中的键?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文