В .NET Framework реализация (override
) Equals(object)
и GetHashCode()
для типов с плавающей точкой (System.Double
и System.Single
) неверна. Чтобы процитировать из MSDN GetHashCode(object)
спецификацию:
Функция хеширования должна иметь следующие свойства:
• Если два объекта сравниваются как равные, метод GetHashCode для каждого объекта должен вернуть одно и то же значение. Однако, если два объекта не сравниваются как равные, Методы GetHashCode для двух объектов не должны возвращать разные значения.
Если вы берете два значения NaN
с разными двоичными представлениями, два объекта сравниваются равными по методу Equals
, но хэш-коды (почти всегда) отличаются.
Теперь эта ошибка содержит в Microsoft Connect. Но почему они не исправят это?
Исправить легко: либо пусть разные NaN
не сравниваются как равные, либо выберите фиксированный хеш-код для возврата для любого NaN
.
Исправление ничего не сломает: как это происходит сегодня, ничего не работает, когда используются разные NaN
.
Можете ли вы подумать о какой-либо причине, чтобы не исправить это?
Вот простой пример, иллюстрирующий текущее поведение:
using System;
using System.Collections.Generic;
using System.Linq;
static class Program
{
const int setSize = 1000000; // change to higher value if you want to waste even more memory
const double oneNaNToRuleThemAll = double.NaN;
static readonly Random randomNumberGenerator = new Random();
static void Main()
{
var set = new HashSet<double>(); // uses default EqualityComparer<double>
while (set.Count < setSize)
set.Add(GetSomeNaN());
Console.WriteLine("We now have a set with {0:N0} members", set.Count);
bool areAllEqualToTheSame = set.All(oneNaNToRuleThemAll.Equals);
if (areAllEqualToTheSame)
Console.WriteLine("By transitivity, all members of the set are (pairwise) equal.");
}
static double GetSomeNaN() // can also give PositiveInfinity, NegativeInfinity (unlikely)
{
byte[] b = new byte[8];
randomNumberGenerator.NextBytes(b);
b[7] |= 0x7F;
b[6] |= 0xF0;
return BitConverter.ToDouble(b, 0);
}
}
Результат запуска кода: миллион дубликатов в HashSet<>
.
ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ: ничего не имеет отношения к операторам ==
и !=
оператора С#. Используйте Equals
, если вы хотите проверить это самостоятельно.