2012-07-02 7 views
16

Duplicar posibles:
C# Why can equal decimals produce unequal hash values?Decimal.GetHashCode Depende de ceros a la derecha

Me he encontrado con un problema en mi aplicación .NET 3.5 (x86 o x64, he tratado ambos) donde los decimales con un número diferente de ceros finales tienen diferentes códigos hash. Por ejemplo:

decimal x = 3575.000000000000000000M; 
decimal y = 3575.0000000000000000000M; 

Console.WriteLine(x.GetHashCode()); 
Console.WriteLine(y.GetHashCode()); 
Console.WriteLine(x == y); 
Console.WriteLine(x.GetHashCode() == y.GetHashCode()); 

da el siguiente resultado en mi máquina:

1085009409 
1085009408 
True 
False 

supongo que la diferencia en los códigos hash se ha reducido a las diferentes representaciones internas de los dos números causadas por los factores de escala diferentes.

Aunque puedo evitar el problema eliminando los ceros finales, siempre asumí que GetHashCode debería devolver el mismo valor para xey, si x == y. ¿Esta suposición es incorrecta, o es un problema con Decimal.GetHashCode?

EDITAR: Para tener claridad en las versiones, estoy usando Visual Studio 2008 SP1, .NET 3.5.

+1

¿Es este su código real? Esto devuelve '1085009408, 1085009408, True True' para mí. - Editar: eso fue .NET 4, diferentes resultados en .NET 3.5 confirmado. – CodeCaster

+0

Obtuve el mismo resultado que el OP en .NET 3.5. @CodeCaster, ¿en qué versión lo está ejecutando? – Servy

+1

Así que fui y miré los bits del decimal y son diferentes para xey (con .NET pre 3.5). Claramente, el método 'Equals' explica esta diferencia, pero' GetHashCode' no. – Servy

Respuesta

12

Esto es un problema con Decimal.GetHashCode, para .NET Framework versión 3.5 o inferior. Cuando dos valores se consideran iguales, deben devolver el mismo código hash, según las pautas; en este caso, decimal claramente no lo hace. Siempre debe esperar que dos objetos iguales tengan el mismo código hash.

Per MSDN:

Si dos objetos resultan ser iguales, el método GetHashCode para cada objeto debe devolver el mismo valor.

que reproduce

me han tratado el código exacto contra diferentes versiones de .NET Framework, y los resultados son los siguientes:

╔══════════════════╤══════════════════╗ 
║Framework version │ Hashcode equal ? ║ 
╟──────────────────┼──────────────────╢ 
║  2.0   │ No.    ║ 
║  3.0   │ No.    ║ 
║  3.5   │ No.    ║ 
║  4.0   │ Yes.   ║ 
║  4.5   │ Yes.   ║ 
╚══════════════════╧══════════════════╝ 

En otras palabras, parece que tropezó con un error en .NET Framework, que se corrigió con .NET Framework 4.

Los resultados anteriores se alcanzaron con Visual Studio 2012 RC, utilizando las páginas de propiedades para cambiar el marco.

Microsoft acknowledges the bug here.

+0

Vi esos documentos también, así que supuse que debía tratarse de un error, pero pensé en obtener algunas otras opiniones aquí primero; ¡los errores en .NET son bastante raros después de todo! – MrKWatkins

+0

Parece que es un error, que se soluciona con la versión actual; ver la actualización de la respuesta. – driis

+0

Gracias por su trabajo aquí, muy apreciado. Otra razón para actualizar a .NET 4 ... – MrKWatkins

9

Esto era bastante infamous bug en versiones .NET anteriores a .NET 4. La implementación Decimal.GetHashCode() tenía una dependencia en los valores de bit en el valor decimal. Son diferentes, ya que las pistas decimales son el número de dígitos conocidos en la fracción. Algo que puede ver al usar Decimal.GetBits() en los valores. De hecho, es discutible si se trata de un error, los decimales do tienen valores diferentes, dependiendo del tipo de gafas que use.

Sin embargo, Microsoft acordó que esto era un comportamiento poco intuitivo y lo corrigió en .NET 4, el artículo relevante de comentarios is here.

+0

Me di cuenta del problema debido a jugar con GetBits; desde ese punto de vista, definitivamente son muy diferentes, lo que me ha estado causando algunos dolores de cabeza ... No me di cuenta del problema de GetHashCode por un tiempo, aunque en cuanto a un montón de ceros finales, producen el mismo código hash. .. Por ejemplo 3575 con 0 -> 17 o 19 -> 22 ceros al final todos dan el mismo código hash pero tienen resultados GetBits() bastante diferentes. Me encantaría ver la implementación real de GetHashCode para poder averiguar por qué ... – MrKWatkins

+1

Descargar SSCLI20, archivo de código fuente clr/src/vm/comdecimal.cpp, función COMDecimal :: GetHashCode(). –

+0

Excelente, ¡gracias! – MrKWatkins