简体   繁体   English

提高::哈希 <double> RHEL 5.4 64位上的异常行为

[英]boost::hash<double> odd behavior on RHEL 5.4 64bit

I wrote a simple code to understand boost::hash behavior, the source code as following: 我编写了一个简单的代码来了解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;
  }
}

Scenario #1: running in release build (gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)), the output looks like: 场景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

Scenario #2: running in debug build (same compiler), the output is: 场景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

Is the behavior of release build normal? 发布版本的行为是否正常? in Scene #1, hash of 1, 2, 3 are of the same, and hash of 1 while running for 3 times are different! 在场景#1中,1、2、3的哈希值相同,并且连续运行3次时1的哈希值不同! How could I make it work properly like in debug build? 如何使它像在调试版本中一样正常工作? Could anyone please shed me some light? 谁能给我一些启示? Thanks. 谢谢。

The GCC option line is: g++ -fnon-call-exceptions -O2 -I include/ main.cpp, if I remove fnon-call-exception option, this problem goes. GCC选项行是:g ++ -fnon-call-exceptions -O2 -I include / main.cpp,如果我删除fnon-call-exception选项,则会出现此问题。

Edited 编辑

Following is kinda a final version workaround: 以下是最终版本的解决方法:

  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
  }

Hope it's useful for who met with the same problem and couldn't update their compiler. 希望对遇到相同问题且无法更新其编译器的人有用。

@David Schwartz, it shall not be a bug of boost, this issue can be reproduced with a simpler code: @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;
  }
}

Check its assembly code (with g++ -S) you'll see following: 检查其汇编代码(使用g ++ -S),您将看到以下内容:

.LCFI0:
        movq    (%rsp), %rdi
        movsd   %xmm0, (%rsp)
        call    _Z4hashm
        addq    $8, %rsp
        ret

Here %rdi is messed up. 这里%rdi搞砸了。 My conclusion is: not to transfer an re-interpreted data of double as a function parameter. 我的结论是:不要将double的重新解释数据作为函数参数进行传输。

No, this behaviour is not normal. 不,这种行为是正常的。

Your code is fine. 您的代码很好。

Ergo: it's a bug in the compiler. Ergo:这是编译器中的错误。

It's not reproducible in any recent version of the compiler, so the logical course of action would be to upgrade. 它在任何最新版本的编译器中均不可复制,因此,合理的做法是升级。 (I don't think GCC will accept new bug reports on this old version) (我认为GCC不会接受有关此旧版本的新错误报告)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM