我写了一个简单的代码来理解boost :: hash行为,源代码如下:
#include <iostream>
#include "boost/functional/hash.hpp"
namespace myns {
size_t get_hash(double v) {
boost::hash<double> haser;
return haser(v);
}
}
int main() {
double arr[] = {1.0, 1.0, 2.0, 1.0, 3.0, 2.0};
for (int i = 0; i < 6; i++) {
std::cout << "Hash for " << arr[i] << " is " << myns::get_hash(arr[i]) << std::endl;
}
}
场景#1:在发布版本中运行(gcc版本4.1.2 20080704(Red Hat 4.1.2-46)),输出如下:
Hash for 1 is 140736533409552
Hash for 1 is 4607182418800017408
Hash for 2 is 4607182418800017408
Hash for 1 is 4611686018427387904
Hash for 3 is 4607182418800017408
Hash for 2 is 4613937818241073152
场景#2:在调试版本(相同的编译器)中运行,输出为:
Hash for 1 is 4607182418800017408
Hash for 1 is 4607182418800017408
Hash for 2 is 4611686018427387904
Hash for 1 is 4607182418800017408
Hash for 3 is 4613937818241073152
Hash for 2 is 4611686018427387904
发布版本的行为是否正常?在场景#1中,1,2,3的散列是相同的,并且在运行3次时散列为1是不同的!如何在调试版本中使其正常工作?有谁能请我解释一下?感谢。
GCC选项行是:g ++ -fnon-call-exceptions -O2 -I include / main.cpp,如果我删除了fnon-call-exception选项,就会出现这个问题。
答案 0 :(得分:1)
<强>被修改强>
以下是最终版本的解决方法:
size_t get_hash(double v) {
#if (__GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ == 2)
switch(std::fpclassify(v))
{
case FP_ZERO:
return 0;
case FP_INFINITE:
return (std::size_t)(v > 0 ? -1 : -2);
case FP_NAN:
return -3;
case FP_NORMAL:
case FP_SUBNORMAL:
#if defined(__x86_64__)
return *((size_t*)&v);
#else
long long ll = *((long long*)&v);
size_t seed = ll & 0xFFFFFFFF;
seed ^= (ll>>32) + (seed<<6) + (seed>>2);
return seed;
#endif
default:
assert(false);
return 0;
}
#else
boost::hash<double> haser;
return haser(v);
#endif
}
希望对遇到同样问题并且无法更新编译器的用户有用。
@David Schwartz,它不应该是一个提升的错误,这个问题可以用更简单的代码重现:
#include <iostream>
size_t hash(size_t x) {
return x;
}
size_t hash(double d) {
size_t x = *(size_t*)&d;
return hash(x);
}
int main() {
double arr[] = {1.0, 1.0, 2.0, 1.0, 3.0, 2.0};
for (int i = 0; i < 6; i++) {
std::cout << "Hash for " << arr[i] << " is " << hash(arr[i]) << std::endl;
}
}
检查其汇编代码(使用g ++ -S),您将看到以下内容:
.LCFI0:
movq (%rsp), %rdi
movsd %xmm0, (%rsp)
call _Z4hashm
addq $8, %rsp
ret
这里%rdi搞砸了。我的结论是:不要将double的重新解释数据作为函数参数传递。
答案 1 :(得分:0)
不,此行为不正常。
你的代码很好。
Ergo:这是编译器中的一个错误。
它在任何最新版本的编译器中都不可重现,因此逻辑行动方针是升级。 (我不认为GCC会接受关于这个旧版本的新bug报告)