我遇到了相等的问题并将对象添加到字典
class DoublePoint
{
public double X;
public double Y;
public double Z;
public DoublePoint(double x, double y, double z)
{
this.X = x; this.Y = y; this.Z = z;
}
public override bool Equals(object obj)
{
try
{
DoublePoint dPoint = obj as DoublePoint;
return this.X.IsEqualTo(dPoint.X) && this.Y.IsEqualTo(dPoint.Y) && this.Z.IsEqualTo(dPoint.Z);
}
catch
{
throw;
}
}
public override int GetHashCode()
{
return this.X.GetCode() ^ this.Y.GetCode() ^ this.Z.GetCode();
}
}
static class extensions
{
static double Tolerance = 0.001;
public static bool IsEqualTo(this double d1, double d2)
{
return (d1 - d2) <= Tolerance;
}
public static int GetCode(this double d1)
{
byte[] data = BitConverter.GetBytes(d1);
int x = BitConverter.ToInt32(data, 0);
int y = BitConverter.ToInt32(data, 4);
return x ^ y;
}
}
这是我的测试:
DoublePoint d1 = new DoublePoint(1.200, 2.3, 3.4);
DoublePoint d2 = new DoublePoint(1.2001, 2.3, 3.4);
DoublePoint d3 = new DoublePoint(1.200, 2.3, 3.4);
bool isEqual = d1.Equals(d2); // true here
Dictionary<DoublePoint, int> dict = new Dictionary<DoublePoint, int>();
dict.Add(d1, 1);
dict.Add(d2, 2); // successful, d2 is also added but d2 is equal to d1
dict.Add(d3, 3); // Error! since we have d1 already in dictionary
有了这个,
当我添加相同(具有一定容差)的双点对象时,我可以将它们添加到字典中。如何限制这些对象。
是比较具有一定容差的双数据类型的正确方法。
请指教。
谢谢
答案 0 :(得分:1)
将“相等”定义为“足够接近”存在问题。毫无疑问,这对于计算是有用的,但是这种“相等”违反了及物性规则:Equals
如果a.Equals(b) && b.Equals(c)
,那么a.Equals(c)
必须保持为真(这是显然不是你代码的属性。)
因此,IsEqualTo
不适合重新定义Equals
。
解决问题的可能方法有哪些? Equals
必须分成不相交的“等价”值组。我通常会执行以下操作:定义规则以从组中获取“规范”值,因此如果规范组代表相等,则两个值“相等”。
简单示例:仅使用双精度值d
,我们将规范值定义为Math.Floor(d)
。所以这种方式你有1.0等于1.1,0.9等于0.0但不等于1.0。这种方式不是理想的方法(毕竟,0.9不等于1.0但等于0.0似乎是错误的),但至少保留了及物性规则。
特别针对您的情况,可能就是这样:
class DoublePoint
{
public double X;
public double Y;
public double Z;
const double epsilon;
void GetCanonicalValues(out double x, out double y, out double z)
{
x = Math.Floor(X / epsilon) * epsilon;
y = Math.Floor(Y / epsilon) * epsilon;
z = Math.Floor(Z / epsilon) * epsilon;
}
public override bool Equals(object obj)
{
DoublePoint that = obj as DoublePoint;
if (that == null)
return false;
double x1, y1, z1, x2, y2, z2;
this.GetCanonicalValues(out x1, out x2, out z2);
that.GetCanonicalValues(out x1, out x2, out z2);
return (x1 == x2) && (y1 == y2) && (z1 == z2); // here we can compare
}
...
您的代码的另一个问题是,您的GetHashCode
未与Equals
对齐:如果a.Equals(b)
则a.GetHashCode()
必须等于{{1} }}
您也可以使用规范值解决此问题:
b.GetHashCode()
请注意 public override int GetHashCode()
{
double x, y, z;
GetCanonicalValues(out x, out y, out z);
return x.GetHashCode() ^ y.GetHashCode() ^ z.GetCode();
}
}
的行为可能对您的需求不可接受 - 然后您需要以其他方式确保传递性。