std :: hash是否保证“相等”浮点数的哈希值相等?

时间:2013-02-18 19:25:12

标签: c++ c++11 hash floating-accuracy stdhash

std::hash的{​​{1}}(例如doublefloat s)的浮点专精化是否可靠almost-equality?也就是说,如果两个值(例如(1./std::sqrt(5.)/std::sqrt(5.)).2)应该相等但不会与==运算符进行比较,那么std::hash将如何表现?

那么,我可以依靠double作为std::unordered_map键来按预期工作吗?


我看过“Hashing floating point values”,但是要求提升;我在询问C ++ 11的保证。

4 个答案:

答案 0 :(得分:9)

std::hash对所有类型都有相同的保证 实例化:如果两个对象相等,它们的哈希码将是 等于。否则,它们的可能性非常大 惯于。因此,您可以依赖double作为关键字 unordered_map按预期工作:如果没有两个双打 相等(由==定义),它们可能会有所不同 哈希(即使它们没有,它们也是不同的键,因为 unordered_map也检查是否相等。

显然,如果你的价值观是不准确的结果 计算,它们不是unordered_map的合适键 (也许不是任何地图)。

答案 1 :(得分:7)

这个问题存在多个问题:

  • 你的两个表达式不比较相等的原因并不是有两个二进制表达式为0.2,而是没有0.2的完全(有限)二进制表示,或{{ 1}}!实际上,虽然sqrt(5)(1./std::sqrt(5.)/std::sqrt(5.))在代数上应该是相同的,但在计算机精度算术中它们可能不一样。 (它们甚至不具有有限精度的纸笔算法。假设您使用小数点后的10位数。用10位数写出.2并计算您的第一个表达式。它不会是{ {1}}。)

  • 当然,你有两个数字接近的明智概念。事实上,你至少有两个:一个绝对(sqrt(5)),一个亲戚。但这并没有转化为明智的哈希。如果您希望彼此的.2内的所有数字具有相同的散列,则|a-b| < eps将具有相同的散列,因此,所有数字将具有相同的散列。这是一个有效但无用的哈希函数。但它是唯一一个满足您将附近值映射到同一个哈希值的要求!

答案 2 :(得分:3)

没有严格的“几乎平等”的概念。所以原则上不能保证行为。如果你想定义你自己的“几乎相等”的概念并构造一个哈希函数,使得两个“几乎相等”的浮点数具有相同的哈希值,你可以。但是,只有你的“几乎相同”浮动的概念才会出现这种情况。

答案 3 :(得分:2)

unordered_map的默认散列后面有一个std::hash结构,它提供operator()来计算给定值的散列。

此模板的一组默认专精可用,包括std::hash<float>std::hash<double>

在我的机器上(LLVM + clang),这些被定义为

template <>
struct hash<float> : public __scalar_hash<float>
{
    size_t operator()(float __v) const _NOEXCEPT
    {
        // -0.0 and 0.0 should return same hash
       if (__v == 0)
           return 0;
        return __scalar_hash<float>::operator()(__v);
    }
};

其中__scalar_hash定义为:

template <class _Tp>
struct __scalar_hash<_Tp, 0> : public unary_function<_Tp, size_t>
{
    size_t operator()(_Tp __v) const _NOEXCEPT
    {
        union
        {
            _Tp    __t;
            size_t __a;
        } __u;
        __u.__a = 0;
        __u.__t = __v;
        return __u.__a;
    }
};

基本上通过将union的值设置为源值然后只获得一个大size_t的片段来构建散列。

所以你得到一些填充或者你的值被截断,但这并不重要,因为你可以看到数字的原始位用于计算哈希值,这意味着它的工作原理与==运营商。两个浮动数字,具有相同的散列(不包括由截断给出的碰撞),必须是相同的值。