Dot-Net

C# Decimal.GetHashCode() 和 Double.GetHashCode() 相等

  • August 26, 2015

為什麼是

17m.GetHashCode() == 17d.GetHashCode()

(m=decimal, d=double)

另外,正如預期的那樣

17f.GetHashCode() != 17d.GetHashCode()

(f=float)

這對於 net3.5 和 net4.0 似乎都是正確的。

據我了解,這些類型的內部位表示是完全不同的。那麼為什麼十進制雙精度類型的雜湊碼對於相同的初始化值是相等的呢?在計算雜湊之前是否發生了一些轉換?

我發現原始碼Double.GetHashCode()是這樣的:

//The hashcode for a double is the absolute value of the integer representation 
//of that double. 
//  
[System.Security.SecuritySafeCritical]  // auto-generated 
public unsafe override int GetHashCode() {  
   double d = m_value;  
   if (d == 0) { 
       // Ensure that 0 and -0 have the same hash code  
       return 0; 
   } 
   long value = *(long*)(&d); 
   return unchecked((int)value) ^ ((int)(value >> 32));  
} 

我驗證此程式碼返回所需的值。但是我沒有找到Decimal.GetHashCode(). 我嘗試使用方法

public static unsafe int GetHashCode(decimal m_value) {  
   decimal d = m_value;  
   if (d == 0) { 
       // Ensure that 0 and -0 have the same hash code  
       return 0; 
   } 
   int* value = (int*)(&d);
   return unchecked(value[0] ^ value[1] ^ value[2] ^ value[3]);  
} 

但這與預期的結果不匹配(它返回了與類型對應的雜湊,考慮到decimal 的內部佈局int,這也是預期的)。所以我目前還不知道它的實施。Decimal.GetHashCode()

Decimal.GetHashCode() 方法在 CLR 中實現。您可以從 SSCLI20 原始碼 clr/vm/comdecimal.cpp 了解可能的實現:

double dbl;
VarR8FromDec(d, &dbl);
if (dbl == 0.0) {
   // Ensure 0 and -0 have the same hash code
   return 0;
}
return ((int *)&dbl)[0] ^ ((int *)&dbl)[1];

否則,這與 C# 中的Double.GetHashCode()實現完全等效,但用 C++ 編寫,因此獲得匹配並不意外。 VarR8FromDec()是一個 COM 自動化幫助函式,可將 COM DECIMAL 轉換為雙精度。

當然,永遠不要依賴這樣的比賽。


更新:現在 CLR 是開源的,在這個 github 文件中可見,它看起來仍然一樣。一個問題是 VarR8FromDec() 是一個 Windows 函式,在 Linux 或 OSX 中不可用,它在 PAL 中重新實現

引用自:https://stackoverflow.com/questions/12236499