有趣的stdext :: hash_value()实现

时间:2011-04-01 16:47:17

标签: c++ visual-studio-2010 hash c++11

我在VS2010上使用stdext::hash_value()尝试了一些散列算法,并实现了这一点:

#include <iostream>
#include <xhash>
using namespace std;

int main() 
{
#ifdef _WIN64
    std::cout << "x64" << std::endl;
#else
    std::cout << "x32" << std::endl;
#endif

    std::cout << stdext::hash_value(345.34533) << std::endl;
    std::cout << stdext::hash_value(345.566) << std::endl;

    return 0;
}

// Output is:
// x64
//3735928758
//3735928758

我尝试了一些其他具有相同整数但不同小数部分的双变量对。喜欢1.234 vs 1.568。哈希值始终相同。所以我看一下hash_value()的来源并看到了

#define _HASH_SEED  (size_t)0xdeadbeef

template<class _Kty> inline
size_t hash_value(const _Kty& _Keyval)
{   // hash _Keyval to size_t value one-to-one
    return ((size_t)_Keyval ^ _HASH_SEED);
}

_KeyVal被投射到size_t,这对我没有意义。该函数只是忽略double的小数部分。这种实施背后的逻辑是什么?这是一个错误或功能吗?

5 个答案:

答案 0 :(得分:2)

stdext :: hash_value不是哈希函数。它是哈希函数的输入,您将它专门用于您的类型,以便它可以用作stdext哈希类的键。但是,它似乎没有任何文件。实际的哈希函数是stdext :: hash_compare。

但是因为没有hash_value的默认特化,所以它使用convert-to-int方法忽略小数部分。

在vc10之前,标准的std :: tr1 :: hash / std :: hash函数有一个几乎完全相同的错误:

http://connect.microsoft.com/VisualStudio/feedback/details/361851/std-tr1-hash-float-or-hash-double-is-poorly-implemented

在vc10中,std :: hash得到一个哈希位的双重特化。我想stdext现在已经过时了,所以即使在vc10中也没有解决它。

答案 1 :(得分:1)

编写该函数以使用任何类型的数据。它没有对大小做出假设,因此对某些类型来说效率低下。您可以为双精度覆盖此行为,以通过模板专业化提高效率

template<>
size_t hash_value<double>(const double& key) {
    return // Some awesome double hashing algorithm 
}

将此定义置于main方法之上将导致对stdext::hash_value(354.566)的调用绑定到此定义,而不是标准

答案 2 :(得分:0)

这是旧代码 - 似乎不太好。

您应该尝试使用std :: hash。

答案 3 :(得分:0)

显然,这是尝试为其提供通用散列函数 整数(虽然我不知道xor添加了什么)。很清楚 不适用于大多数其他类型。包括浮点。

为浮点值提供良好的散列函数是困难的; 如果我试图制作通用哈希,我可能会从测试开始 对于0,NaN和Inf,并返回那些(或 完全拒绝NaN,因为它不是有效的哈希值),然后 只需在底层字节上使用标准字符串哈希。这将 至少使哈希与==运算符兼容。但问题 精度意味着==本身可能不是所需要的。也不是 std :: map的情况,因为std :: map使用&lt;定义一个平等 关系,并根据浮动或双打的来源,如此 等式关系可能不适合哈希表。

无论如何,不​​要期望为浮点类型找到标准哈希函数。

答案 4 :(得分:0)

VC10包含C ++ 0x标准散列机制,因此不再需要使用stdextstd::hash确实包含执行按位转换的double机制然后是哈希。您拥有stdext的代码只是一种回退机制,并不适合用于浮点类型。我想这是一个设计疏忽。

template<>
class hash<double>
    : public unary_function<double, size_t>
{   // hash functor
public:
typedef double _Kty;
typedef _ULonglong _Inttype;    // use first 2*32 bits

size_t operator()(const _Kty& _Keyval) const
    {   // hash _Keyval to size_t value by pseudorandomizing transform
    _Inttype _Bits = *(_Inttype *)&_Keyval;
    return (hash<_Inttype>()(
        (_Bits & (_ULLONG_MAX >> 1)) == 0 ? 0 : _Bits));
    }
};