开发者

How should I implement .Equals() in my Vector3 structure?

I have an immutable Vector3 structure, and I'm wondering how to best implement the .Equals() method so that it's useful and still satisfies the Guidelines for Overloading Equals().

Here's a partial implementation of my structure:

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

Edit: I don't want to directly compare each X, Y, Z due to the nature of floating point operations. For example, I can tell if two vector3s are parallel by checking to see if u x v == <0, 0, 0>; however with floating point operations this will often fail because one of the zeros is actually <8.205348E-09>.

I'm looking for an Equals() method that is a bit smart开发者_如何学Goer. I want it to work whether the numbers are very large or very small. I


In NGenerics http://code.google.com/p/ngenerics/ we have a base class VectorBase

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

Then we have a Vector3D which inherits from VectorBase<

class Vector3D : VectorBase<double>

The full list of vectors is

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

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


Unless there is some minimum-precision between vectors that is implied with the Vector3, I would make Equals be a simple FP equality check.

In addition I would create an AlmostEquals method (possibly even an overloaded Equals which is free from the contract of Equals). I would not put "fuzzy logic" into Equals because Equals as a strict contract with GetHashCode.

On the other hand, if a minimum-precision requirement is set, that could be used and would be just a normal equality check of the normalization. The reason to do it after normalization would be so that GetHashCode could also used the normalized values. (Just delta-checks across the values would not work for GetHashCode). I'm not a fan of this approach though.

Of course the approach used should take into account how the Vector3 is to be used and what role it has. Perhaps there is a need for a more restricted type?


What is wrong with the following?

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


You shouldn't implement Equals in Vector3 by using distance checks. First of all this will break "if (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true." contract. Do you need an example for this?.
Second, why do you need to overload Equals method? Who will be the client of that API? Do you want to compare these objects by yourself or you want to use some libraries like collections or maps with instances of Vector3 as values and/or keys? In later case this can be very dangerous! If you want to use such vectors as keys in hash maps when you will also need to define trivial GetHashCode method which will return constant. So the best comparison will be "this._x == vector._x && this._y == vector._y && this._z == vector._z" in that case.
If you want to have such comparison for yourself then it will be more clear if you will define some other method like "IsClose" with explanation of comparison idea in method summary.


"Would normalizing both vectors and then comparing each float within a small epsilon value work well?"

Yes it should work quite well. However there is better (more efficient) way to do this. Extract exponents of compared numbers, then compare subtracted value to eps rised to the max exponents power.

Pseudo code would look like this:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜