当前位置:  开发笔记 > 编程语言 > 正文

有没有办法用epsilon获取浮点数的哈希码?

如何解决《有没有办法用epsilon获取浮点数的哈希码?》经验,为你挑选了3个好方法。

众所周知,通过==比较浮点数通常是一个错误.在我写的3D矢量类(具有浮点分量X,Y,Z)中,如果它们的距离被认为是零,则认为两个矢量相等.

    public override bool Equals(object obj)
    {
        if (obj == null) {
            return false;
        }

        if (GetType () != obj.GetType ()) {
            return false;
        }

        float d = DistSq ((Vec) obj);

        return IsConsideredZero (d);
    }

    public float DistSq(Vec p)
    {
        Vec d = this - p;
        return d.LengthSq ();
    }

    public float LengthSq()
    {
        return X * X + Y * Y + Z * Z;
    }

    private const float VEC_COMPARE_EPSILON_ABS = 1E-05f;
    public static bool IsConsideredZero(float f)
    {
        return Math.Abs (f) < VEC_COMPARE_EPSILON_ABS;
    }

到目前为止,一切正常.但是,现在我想获得向量的哈希码.我可以看到类似的东西hash = (int)X^(int)Y^(int)Z一定会失败.

我能想到的最好的是:

    public override int GetHashCode()
    {
        return 0;
    }

当然,这有点糟透了.有没有办法获得合理的哈希码?NaNs和其他特殊值是可能的,但不太可能,如果这很重要的话.



1> Jon Skeet..:

假设您想要具有正常的哈希码/相等属性,这是不可能的:

如果X = Y且Y = Z则X = Z(传递性)

如果X = Y则Y = X(传播)

X = X表示所有X(反身性)

第一个规则是问题 - 因为如果每个值被认为与下一个更大的可表示数字"相等",那么最终所有数字都相等.例如,假设一个数字被认为等于另一个数字,它们在0.1以内:

0等于0.08 0.08等于0.16 0.16等于0.24

传递规则=> 0等于0.16 =传递规则=> 0等于0.24

(等等)

如果忽略传递性规则,那么您(可能)仍希望"相等"值具有相同的哈希码.这有效地强制执行传递规则 - 在上面的例子中,0和0.08必须具有相同的哈希码,0和0.16也是如此.因此0和0.16必须具有相同的哈希码,依此类推.因此,您可以没有有用的哈希码 - 它必须是常量.



2> David Z..:

我不认为你可以有一个与你的比较方法一致的哈希码,因为后者不是传递性的:对于任何三个向量A,B,C,if A.Equals(B)B.Equals(C)为真,它仍然可能A.Equals(C)是假的.(想象一下,如果A和B之间的距离是6e-6,B和C之间的距离是6e-6,A和C之间的距离是1.2e-5)但是哈希码的相等总是可传递的,因为它们只是数字.

在这种情况下,我只是创建一个哈希码方法,根据浮点坐标的精确值计算哈希值,并在文档中提到它与equals不一致.我知道这不是一个真正的解决方案,但考虑到我认为不存在真正的解决方案,最好有一个非平凡的哈希码,而不仅仅是0.



3> Nick Fortesc..:

我担心这不是一般情况.证明的草图如下:

取两个数字a和b.让他们之间的区别是d.然后,如果您创建d/epsilon数字,其间有一个epsilon步骤,则每个步骤必须与之前的步骤"相等",这通过哈希码语义具有相同的哈希码.所以所有数字必须具有相同的哈希码.

如果添加其他约束,则只能解决此问题.

顺便说一句,你对Equals的定义也是错误的,因为a.Equals(b)和b.Equals(c)可能是真的,而不是a.Equals(c),这对于equals是错误的.这被称为打破Transitivity属性.

那我该怎么办?

解决方案取决于您使用哈希的内容.一种解决方案是引入概念网格.如果在同一个网格立方体中,通过舍入到常数小数位,然后在舍入数字上取等号和哈希码,更改等号和哈希码使两个数字相等.如果接近零是一个重要的情况,在舍入之前添加epsilon/2的偏移量,因此零是立方体的中心.这是正确的,但你可以任意地将两个数字放在一起(在浮点数的限制下)而不是相等的.因此,对于某些应用程序,它将是正常的,而其他应用程序则不会.这类似于mghie的想法.

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有