简体   繁体   中英

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:

#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:

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:

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! 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.

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:

#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:

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

Here %rdi is messed up. My conclusion is: not to transfer an re-interpreted data of double as a function parameter.

No, this behaviour is not normal.

Your code is fine.

Ergo: it's a bug in the compiler.

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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