我如何使用f64的HashMap作为Rust中的键? [英] How can I use a HashMap with f64 as key in Rust?

查看:400
本文介绍了我如何使用f64的HashMap作为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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆