C# Decimal.GetHashCode() and Double.GetHashCode() equal



为什么
17m.GetHashCode() == 17d.GetHashCode()
(m=十进制,d=双精度)
此外,正如预期的那样
17f.GetHashCode() != 17d.GetHashCode()
(f=浮动)
这对于net3.5和net4.0来说似乎都是正确的。

据我所知,这些类型的内部位表示是完全不同的。那么,decimaldouble类型的哈希码为什么对于相等的初始化值相等呢?在计算哈希之前是否进行了一些转换?

我发现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]);  
} 

但这与期望的结果不匹配(它返回了与int类型对应的哈希,考虑到十进制的内部布局,这也是预期的)。因此,Decimal.GetHashCode()的实现目前对我来说仍然是未知的。

Decimal.GetHashCode()方法在CLR中实现。您可以从SSCLI20源代码clr/vm/comdimal.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 Automation辅助函数,用于将COM DECIMAL转换为双精度。

当然,从不依赖这样的匹配。


更新:现在CLR是开源的,在这个github文件中可见,看起来仍然一样。一个问题是VarR8FromDec()是一个在Linux或OSX中不可用的Windows函数,它是在PAL中重新实现的。

最新更新