我应该如何在Vector3结构中实现.Equals()?

时间:2009-11-09 03:18:13

标签: c# floating-point

我有一个不可变的Vector3结构,我想知道如何最好地实现.Equals()方法,以便它有用并且仍然满足Guidelines for Overloading 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。例如,我可以通过检查 u x v ==< 0,0,0&gt ;;来判断两个vector3是否平行;但是对于浮点运算,这通常会失败,因为其中一个零实际上是< 8.205348E-09>。

我正在寻找一种更智能的Equals()方法。无论数字是非常大还是非常小,我希望它可以工作。我

5 个答案:

答案 0 :(得分:2)

在NGenerics http://code.google.com/p/ngenerics/中,我们有一个基类VectorBase

来自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;
    }

然后我们有一个继承自VectorBase&lt;

的Vector3D
class Vector3D : VectorBase<double>

完整的载体列表是

* Vector2D - 2 dimensional vector.
* Vector3D - 3 dimensional vector.
* VectorN - A vector with user-defined dimensions. 

http://code.google.com/p/ngenerics/wiki/Vectors

答案 1 :(得分:1)

以下是什么问题?

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;
}

答案 2 :(得分:1)

除非Vector3暗示的向量之间存在一些最小精度,否则我会将Equals设为简单的FP等式检查。

另外我会创建一个AlmostEquals方法(甚至可能是一个重载的Equals,它不受Equals的约束)。我不会将“模糊逻辑”放入Equals,因为Equals是与GetHashCode的严格契约。

另一方面,如果设置了最小精度要求,则可以使用该要求,这只是标准化的正常等式检查。归一化后执行此操作的原因是GetHashCode也可以使用规范化值。 (只是对值进行delta检查对GetHashCode不起作用)。我不是这种方法的粉丝。

当然,所使用的方法应该考虑如何使用Vector3以及它具有什么角色。也许需要一种更受限制的类型?

答案 3 :(得分:1)

您不应使用距离检查在Vector3中实现Equals。首先这将打破“if(x.Equals(y)&amp;&amp; y.Equals(z))返回true,然后x.Equals(z)返回true。”合同即可。你需要一个例子吗? 其次,为什么需要重载Equals方法? 谁将成为该API的客户?您想要自己比较这些对象,还是想将某些库(如集合或地图)与Vector3实例一起用作值和/或键?在以后的情况下,这可能非常危险!如果你想在哈希映射中使用这样的向量作为键,那么你还需要定义一些返回常量的普通GetHashCode方法。因此,在这种情况下,最好的比较是“this._x == vector._x&amp;&amp; this._y == vector._y&amp;&amp; this._z == vector._z”。
如果你想自己进行这样的比较,那么将更清楚你是否会定义一些其他方法,如“IsClose”,并在方法摘要中解释比较思想。

答案 4 :(得分:0)

“对两个向量进行归一化,然后比较一个小的epsilon值中的每个浮点数都能很好地工作吗?”

是的它应该运作得很好。然而,有更好(更有效)的方法来做到这一点。提取比较数字的指数,然后比较减去的值与上升到最大指数权力的eps。

伪代码看起来像这样:

exp1 = exponentOf(r1)
exp2 = exponentOf(r2)
if absolute_value(r2 - r1) <= epsilon^(max(exp1, exp2)) 
    return equal 
else
    return not_equal