c++ - boost::hash<double> 在 RHEL 5.4 64 位上的奇怪行为

标签 c++ boost hash double

我写了一个简单的代码来理解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的hash是一样的,1跑3次的hash是不一样的!我怎样才能让它像在调试版本中一样正常工作?谁能给我一些启发?谢谢。

GCC 选项行是:g++ -fnon-call-exceptions -O2 -I include/main.cpp,如果我删除 fnon-call-exception 选项,就会出现这个问题。

最佳答案

已编辑

以下是最终版本的解决方法:

  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,应该不是boost的bug,这个问题可以用更简单的代码重现:

#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的一个重新解释的数据作为函数参数传递。

关于c++ - boost::hash<double> 在 RHEL 5.4 64 位上的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23258849/

相关文章:

c++ - 如何在一条语句中定义对数组的引用

c++ - R-->cpp 如何获取 C++ 指针以与 sourceCpp 一起使用(贝尔曼福特算法)

python - g++ -shared 参数似乎导致了段错误

php - 加盐密码安全

ruby - 如何在 Ruby 中初始化哈希的大小?

Python 检查字典中是否定义了键

c++ - 具有模板类的函数模板特化

c++ - A* 寻路与大开放列表一起工作

c++ - BOOST - 类模板队列

c++ - io_service.run() 没有阻塞。服务器已创建,然后立即关闭