Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ssl/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Floating point 如何使用f64作为Rust中的键的HashMap?_Floating Point_Hashmap_Rust - Fatal编程技术网

Floating point 如何使用f64作为Rust中的键的HashMap?

Floating point 如何使用f64作为Rust中的键的HashMap?,floating-point,hashmap,rust,Floating Point,Hashmap,Rust,我想使用一个HashMap,保存一个点到另一个点的距离,该点具有已知的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(); 这似乎非常糟糕,两种解决方案,我自己的结构或浮

我想使用一个
HashMap
,保存一个点到另一个点的距离,该点具有已知的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();
这似乎非常糟糕,两种解决方案,我自己的结构或浮点作为带基数和指数的整数,对于一个键来说似乎都非常复杂

更新: 我可以保证我的密钥永远不会是
NaN
,也不会是无限值。此外,我不会计算我的密钥,只会迭代并使用它们。因此,
0.1+0.2的已知错误应该没有错误≠ 0.3

这个问题的共同点是实现浮点数的全序和相等,区别仅在于散列或迭代。

您可以将
f64
拆分为整数部分和小数部分,并以以下方式存储在结构中:

#[derive(Hash, Eq, PartialEq)]
struct Distance {
    integral: u64,
    fractional: u64
}
剩下的很简单:

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)));
}
距离的定义可以是:

#[derive(Hash, Eq, PartialEq)]
struct Distance((u64, i16, i8));

impl Distance {
    fn new(val: f64) -> Distance {
        Distance(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));

    assert_eq!(map.get(&Distance::new(0.4)), Some(&f64::hypot(4.2, 50.0)));
}
fn main(){
让mut映射:HashMap=HashMap::new();
地图插入(距离:新的(0.4),f64:hypot(4.2,50.0));
地图插入(距离:新的(1.8),f64:hypot(2.6,50.0));
assert_eq!(map.get(&Distance::new(0.4)),Some(&f64::hypot(4.2,50.0));
}

不幸的是,浮点类型相等是:

因此散列也很难,因为相等值的散列应该相等


如果在您的情况下,您有足够小的范围将您的数字放入
i64
中,并且您可以接受精度损失,那么一个简单的解决方案是首先规范化,然后根据规范值定义equal/hash:

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
如下所示,从那时起,您可以使用
Distance
作为Hash映射中的键:

impl Hash for Distance {
    fn hash<H>(&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")
impl距离哈希{
fn散列(&self,state:&mut H),其中H:Hasher{
self.canonicalize().hash(状态);
}
}
fn main(){
设d=距离(0.1+0.2);
设e=距离(0.3);
让mut m=HashMap::new();
m、 插入(d,“你好”);
println!(“{:?}”,m.get(&e));
}
//印刷品:一些(“你好”)

警告:重申一下,只有(a)值的动态范围足够小,可以在
i64
(19位)中捕获,并且(b)动态范围是已知的静态因素,此策略才有效。幸运的是,这适用于许多常见问题,但需要记录和测试…

除了阅读所有其他评论和答案之外,没有任何评论。阅读所有其他评论和答案,了解您可能不想这样做的原因。:

使用std:{collections::HashMap,hash};
#[派生(调试、复制、克隆)]
除非您了解范围(f64),否则结构不满足此条件;
除非你了解危险,否则不要这样做{
fn键(&self)->u64{
self.0.to_位()
}
}
impl hash::对dontuseThat进行哈希,除非您了解范围{
fn散列(&self,state:&mut H)
哪里
H:hash::Hasher,
{
self.key().hash(状态)
}
}
除非您了解危险,否则请为Dontus执行PartialQ{
fn eq(&自我,其他:&不了解这些变化)->bool{
self.key()==其他.key()
}
}
除非您了解范围{},否则请为Dontus执行Eq
fn main(){
设a=dont,除非您了解范围(0.1);
设b=Dont,除非您了解范围(0.2);
设c=dont,除非您了解范围(0.3);
让mut map=HashMap::new();
地图.插入(a,1);
地图.插入(b,2);
println!(“{:?}”,map.get(&a));
println!(“{:?}”,map.get(&b));
println!(“{:?}”,map.get(&c));
}
基本上,如果您想将一个
f64
视为一组没有意义的位,那么我们可以将它们视为一个大小相等的位包,知道如何进行散列和按位比较


当其中一个。

您真的需要按精确的距离获取对象时,请不要感到惊讶?使用浮点数作为键与测试两个是否相等(舍入错误确实会发生)一样是一个坏主意。@Shepmaster的重复:这里可能存在
f64
未实现
Eq
的问题,但我认为问题更深=>即使排除
NaN
,比较两个浮点数是否相等只是自找麻烦。您希望您的键有重复值吗?是否有必要通过哈希映射消除重复数据?最好将
规范化为
f32
,而不是乘以常数并转换为整数,因为
1e-12
2e-15
在当前方案中都映射为
0
,但
f32
中的值不同。它也解决了精度问题,因为它只是在比较过程中进行的。@John:也许,也许不是。这完全取决于你想考虑什么。对于以米为单位的距离测量,
1e-12
是1皮米:如果1皮米的差异与任何类型的地理跟踪相关(例如),我会非常惊讶。这确实是一个领域建模决策。如果您希望保持更高的精度,那么哈希映射查找是有缺陷的,您将需要诸如边界卷、KD树等的内容……我不喜欢这种解决方案;它增加了不必要的不精确性,似乎无法很好地映射到任何领域。如果像这样的四舍五入就足够了,那么一开始就不应该使用浮点数。我不认为如果
f32
加法不能产生准确的预期结果,那么哈希将受到任何影响。毕竟,没有“定律”,它要求
x1+x2==x3
->
x1.hash()+x2.hash()==x3.hash()
。如果舍入错误是应用程序的一个问题,则不要使用浮点,但不要声明由于舍入而无法对浮点值进行散列。使用大块头或任何生锈的东西
fn main() {
    println!("{} {} {}", 0.1 + 0.2, 0.3, 0.1 + 0.2 == 0.3);
}

// Prints: 0.30000000000000004 0.3 false
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
impl Hash for Distance {
    fn hash<H>(&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")