【问题标题】:IEqualityComparer<double> with a tolerance; how to implement GetHashCode?带有容差的 IEqualityComparer<double>;如何实现GetHashCode?
【发布时间】:2012-06-30 19:38:34
【问题描述】:

我正在实现一个可重用的 DoubleEqualityComparer(具有自定义容差:“epsilon”构造函数参数),以简化 LINQ 与 double 序列的使用。例如:

bool myDoubleFound = doubles.Contains(myDouble, new DoubleEqualityComparer(epsilon: 0.01));

什么是实现GetHashCode的正确方法?代码如下:

   public class DoubleEqualityComparer : IEqualityComparer<double>, IEqualityComparer<double?>
    {
        private readonly double epsilon;

        public DoubleEqualityComparer(double epsilon)
        {
            if (epsilon < 0)
            {
                throw new ArgumentException("epsilon can't be negative", "epsilon");
            }

            this.epsilon = epsilon;
        }

        public bool Equals(double x, double y)
        {
            return System.Math.Abs(x - y) < this.epsilon;
        }

        public int GetHashCode(double obj)
        {
            // ?
        }
   }

PS:我总是可以返回相同的值(例如:GetHashCode(double obj){ return 0; })以始终强制调用 Equals(double, double) 方法(我知道,性能不是很好),但我请记住,当比较器与字典一起使用时,此解决方案会导致问题...

【问题讨论】:

  • 你不应该这样做,因为它违反了传递性。 a equals bb equals c 可能是 a not equals c
  • 我有一个类似的问题,关于几何中的点,出于所有意图和目的,如果它们“足够接近”,则认为它们是相等的,因为双精度的存储方式和公差是必需的,所以如果相等您的哈希码应该生成一个相同的哈希,因此当尝试使用字典来跟踪和存储相同的点时,它会由于错误的 GetHashCode 方法而分崩离析。字典有用的原因有很多

标签: .net double hashcode iequalitycomparer epsilon


【解决方案1】:

我不确定使用IEqualityComparer&lt;T&gt; 是否可行。因为比较的对象不相等。

也许您应该考虑使用简单的Any 子句 + 实用方法:

private static bool DoublesAreNearlyEquals(double d1, double d2, double epsilon = 0.01D)
{
    return System.Math.Abs(d1 - d2) < this.epsilon;
}

private void foo()
{
    var myDoubles = Getdoubles();
    var doubleToSearch = 42D;
    var result = myDoubles.Any(d=>DoublesAreNearlyEquals(d, doubleToSearch));
}

【讨论】:

  • 谢谢,你和 Ani 说服我不要使用 IEqualityComparer,而是定义一个自定义接口(及其扩展方法集,LINQ 风格): public interface ITolerable { bool AreAlmostEqual(T x, y); IEqualityComparer 很方便,因为有准备使用的 LINQ 官方方法(不调用 GetHashcode),但我不明白不实现它是可怕的(而且危险的)。
  • @steve 我不同意。 IEqualityComparer(泛型和非泛型)接口的创建正是为了允许自定义定义相等对您的意义。例如,您可以为 double 创建一个自定义相等运算符,它比较近似值但使用原始 double.GetHashCode 生成哈希。如果你想深入,我的建议是看看 StringComparer 类是如何在参考源上实现的。 HTH
【解决方案2】:

我会把NotSupportedException 扔进GetHashCode,这样你就可以吃蛋糕了。这为您在 LINQ 和其他方法中使用 IEqualityComparer 提供了便利,但保证了 GetHashCode 的任何使用都会失败。在实践中,您可能会发现使用相等比较器的方式实际上并不需要调用 GetHashCode。你甚至可以调用这个类 NotHashableDoubleEqualityComparer 来非常清楚对调用者的限制。

【讨论】:

    猜你喜欢
    • 2017-05-20
    • 2014-11-08
    • 1970-01-01
    • 2012-10-23
    • 2011-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多