C# 我应该如何在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; }}

我有一个不可变的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 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