簡體   English   中英

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

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

我編寫了一個簡單的代碼來了解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選項,則會出現此問題。

編輯

以下是最終版本的解決方法:

  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的重新解釋數據作為函數參數進行傳輸。

不,這種行為是正常的。

您的代碼很好。

Ergo:這是編譯器中的錯誤。

它在任何最新版本的編譯器中均不可復制,因此,合理的做法是升級。 (我認為GCC不會接受有關此舊版本的新錯誤報告)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM