C# 我应该如何在Vector3结构中实现.Equals()?
我有一个不可变的Vector3结构,我想知道如何最好地实现.Equals()方法,这样它就很有用,并且仍然满足需要 以下是我的结构的部分实现:C# 我应该如何在Vector3结构中实现.Equals()?,c#,floating-point,C#,Floating Point,我有一个不可变的Vector3结构,我想知道如何最好地实现.Equals()方法,这样它就很有用,并且仍然满足需要 以下是我的结构的部分实现: public struct Vector3 { private float _x; private float _y; private float _z; public float X { get { return _x; }} public float Y { get { return _y; }}
public struct Vector3
{
private float _x;
private float _y;
private float _z;
public float X { get { return _x; }}
public float Y { get { return _y; }}
public float Z { get { return _z; } }
public Vector3(float x, float y, float z)
{
_x = x;
_y = y;
_z = z;
}
public override bool Equals(object obj)
{
//What should go here?
}
}
编辑:由于浮点运算的性质,我不想直接比较每个X,Y,Z。例如,我可以通过检查uxv=,判断两个向量3是否平行;然而,对于浮点运算,这通常会失败,因为其中一个零实际上是零
我正在寻找一个更智能的Equals()方法。我希望无论数字是非常大还是非常小,它都能起作用。我以下有什么问题
protected const float EqualityVariance = 0.0000001;
public override bool Equals(object obj)
{
Vector3? vector = obj as Vector3?;
if (vector == null) return false;
return Equals((Vector3)vector);
}
public bool Equals(Vector3 vector)
{
return Math.Abs(this._x - vector._x) < EqualityVariance &&
Math.Abs(this._y - vector._y) < EqualityVariance &&
Math.Abs(this._z - vector._z) < EqualityVariance;
}
protected const float EqualityVariance=0.0000001;
公共覆盖布尔等于(对象对象对象)
{
Vector3?vector=obj作为Vector3?;
如果(vector==null)返回false;
返回等于((向量3)向量);
}
公共布尔等于(向量3向量)
{
返回Math.Abs(this.\ux-vector.\ux)
在NGenerics中,我们有一个基类VectorBase
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
var vector = obj as IVector<T>;
return (EqualsInternal(vector));
}
public bool Equals(IVector<T> other)
{
return other != null && EqualsInternal(other);
}
private bool EqualsInternal(IVector<T> other)
{
if (dimensionCount != other.DimensionCount)
{
return false;
}
for (var i = 0; i < dimensionCount; i++)
{
if (!Equals(this[i], other[i]))
{
return false;
}
}
return true;
}
public static bool operator ==(VectorBase<T> left, IVector<T> right)
{
// If both are null, or both are same instance, return true.
if (ReferenceEquals(left, right))
{
return true;
}
// If one is null, but not both, return <c>false</c>.
if (((object)left == null) || (right == null))
{
return false;
}
// Return true if the fields match:
return left.EqualsInternal(right);
}
public static bool operator !=(VectorBase<T> left, IVector<T> right)
{
return !(left == right);
}
/// <inheritdoc />
public override int GetHashCode()
{
var hashCode = 0;
for (var index = 0; index < dimensionCount; index++)
{
hashCode ^= this[index].GetHashCode();
}
return hashCode;
}
从矢量基
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
var vector = obj as IVector<T>;
return (EqualsInternal(vector));
}
public bool Equals(IVector<T> other)
{
return other != null && EqualsInternal(other);
}
private bool EqualsInternal(IVector<T> other)
{
if (dimensionCount != other.DimensionCount)
{
return false;
}
for (var i = 0; i < dimensionCount; i++)
{
if (!Equals(this[i], other[i]))
{
return false;
}
}
return true;
}
public static bool operator ==(VectorBase<T> left, IVector<T> right)
{
// If both are null, or both are same instance, return true.
if (ReferenceEquals(left, right))
{
return true;
}
// If one is null, but not both, return <c>false</c>.
if (((object)left == null) || (right == null))
{
return false;
}
// Return true if the fields match:
return left.EqualsInternal(right);
}
public static bool operator !=(VectorBase<T> left, IVector<T> right)
{
return !(left == right);
}
/// <inheritdoc />
public override int GetHashCode()
{
var hashCode = 0;
for (var index = 0; index < dimensionCount; index++)
{
hashCode ^= this[index].GetHashCode();
}
return hashCode;
}
除非向量3中隐含的向量之间存在某种最小精度,否则我将使Equals成为一个简单的FP相等检查。 此外,我将创建一个AlmostEquals方法(甚至可能是一个重载的Equals,它不受Equals契约的约束)。我不会把“模糊逻辑”放在Equals中,因为Equals是一个与GetHashCode严格约定的 另一方面,如果设置了最低精度要求,则可以使用该要求,这只是标准化的正常相等性检查。在规范化之后执行此操作的原因是,GetHashCode也可以使用规范化的值。(对于GetHashCode,仅对值进行增量检查是不起作用的)。不过,我不喜欢这种方法 当然,所使用的方法应该考虑如何使用Vector3以及它的作用。也许需要一种更严格的类型?“规范化两个向量,然后在一个小的ε值内比较每个浮点值是否有效?” 是的,它应该很好用。然而,有更好(更有效)的方法来做到这一点。提取比较数字的指数,然后将减去的值与增加到最大指数幂的eps进行比较 伪代码如下所示:
exp1 = exponentOf(r1)
exp2 = exponentOf(r2)
if absolute_value(r2 - r1) <= epsilon^(max(exp1, exp2))
return equal
else
return not_equal
exp1=指数(r1)
exp2=指数(r2)
如果绝对_值(r2-r1),则不应使用距离检查在向量3中实现相等。首先这将打破“如果(x.Equals(y)和&y.Equals(z))返回true,那么x.Equals(z)返回true”的约定。你需要一个例子吗?
第二,为什么需要重载Equals方法谁将成为该API的客户机?您是想自己比较这些对象,还是想使用一些库(如集合或地图),将Vector3的实例作为值和/或键?在以后的情况下,这可能非常危险!如果您想在哈希映射中使用这些向量作为键,那么还需要定义一个简单的GetHashCode方法,该方法将返回常量。因此,在这种情况下,最好的比较是“this.\ux==vector.\ux&&this.\uy==vector.\uy&&this.\uz==vector.\uz”。
如果您想自己进行这样的比较,那么如果您将定义一些其他方法,例如“IsClose”,并在方法摘要中解释比较思想,则会更加清楚。u还应覆盖==,!=,Equals(Vector)和GetHashCode问题在于,在浮点上执行==通常会导致误判(与当前问题相关)。可能有两个数字应该是1.0,一个可能是0.999999672434。因此,我们可以这样做,而不是a==b,abs(a-b)@Joey:这是实现浮动相等的问题。这在很大程度上取决于你是否想这样做。没有关于用法的额外信息,我仍然推荐我的方法;由于此语句仅适用于引用类型或可空类型使用(Vector3)obj;相反。@AP:说得好。以不产生异常的方式修复。测试所有三个元素的相等性有什么错?有没有理由让它变得更复杂?(假设不需要对Vectorat进行测试)OTOH,考虑到相对精度FP相等可以。。。误导由于“Equals”与“GetHashCode”有约定,我不建议在“Equals”中使用“AlmostEquals”逻辑。Mono的Xna实现对其Vector3类进行严格的Equals检查:
exp1 = exponentOf(r1)
exp2 = exponentOf(r2)
if absolute_value(r2 - r1) <= epsilon^(max(exp1, exp2))
return equal
else
return not_equal