0

I have a vector class that has it's Equals(object obj) method overridden so that I can compare them.

public class Vector3f
{
    public float x,y,z;
    public Vector3f(float x, float y, float z)
    {
       this.x = x;
       this.y = y;
       this.z = z;
    }

    public static Vector3f operator +(Vector3f a, Vector3f b) {
        return new Vector3f(a.x + b.x, a.y + b.y, a.z + b.z);
    }

    public static Vector3f operator -(Vector3f a, Vector3f b) {
        return new Vector3f(a.x - b.x, a.y - b.y, a.z - b.z);
    }
    public override bool Equals(object obj)
    {
        Vector3f other = (Vector3f)obj;
        return x == other.x && y == other.y && z == other.z;
    }

    public override string ToString()
    {
        return String.Format("<{0},{1},{2}>",x,y,z);
    }
}

The plus operator works as expected in my unit tests. However, when I subtract two vectors it says they are not equal

Test 'RTTests.Testies.vector_subtraction_works' failed: 
Expected: <<1.1,0.1,0.1>>
But was:  <<1.1,0.1,0.1>>
Testies.cs(60,0): at RTTests.Testies.vector_sub_works()

I'm not sure why the comparison is working for addition and not subtraction especially since the output values are identical in both cases?

EDIT: My tests for this

    [Test]
    public void vector_addition_works()
    {
        Vector3f v1 = new Vector3f(1.0f, 1.0f, 1.0f);
        Vector3f v2 = new Vector3f(1.6f, 3.2f, 4.7f);

        Vector3f expected = new Vector3f(2.6f, 4.2f, 5.7f);
        Vector3f actual = v1 + v2;

        Assert.AreEqual(actual, expected);
    }

    [Test]
    public void vector_sub_works()
    {
        Vector3f v1 = new Vector3f(1.1f, 1.1f, 1.1f);
        Vector3f v2 = new Vector3f(0.0f, 1.0f, 1.0f);

        Vector3f expected = new Vector3f(1.1f, 0.1f, 0.1f);
        Vector3f actual = v1 - v2;

        Assert.AreEqual(actual, expected);
    }
Matt Phillips
  • 11,249
  • 10
  • 46
  • 71
  • Perhaps you should post the testing procedure that fails as well. – Adam Robinson Mar 17 '11 at 03:41
  • I agree with @Eugenio...I think this is an issue with floating-point arithmetic. You might try using the same float values for both the addition and subtraction to see if it alters either result. In the end, though, I'd either go with the comparison with epsilon that you have below or move to a `decimal` for your values (though that will increase the size of your class, since a `float` is 32 bits, whereas a `decimal` is 128) – Adam Robinson Mar 17 '11 at 03:51

3 Answers3

2

Your problem must be a rounding/truncation error. It happens all the time with floating point operations, specially subtraction. When you test for equality, instead of a==b, use a-b < SmallConstant. You could also try using double precision or Decimal, although truncation errors will eventually return, but you might make them less common.

Eugenio De Hoyos
  • 1,755
  • 16
  • 22
  • even with simple numbers like 1.0f - .5f? – Matt Phillips Mar 17 '11 at 03:37
  • @Matt: Yes. A good rule of thumb is *never* to do equality checking on floating point numbers, only greater than or less than comparisons. If you need a dependable level of precision, use `decimal`. – Adam Robinson Mar 17 '11 at 03:39
  • It usually happens after you have performed several operations on them, but that could depend on your machine. You could test it yourself by debugging each line step by step. You could also perform the equalities separately, and then see which one is the one failing. eg: test1=x1==x2; test2=y1==y2; result=test1&&test2; – Eugenio De Hoyos Mar 17 '11 at 03:40
  • so how small of a constant do you suggest to use? I don't really need more than 4 decimal places for what I'm using them for. – Matt Phillips Mar 17 '11 at 03:49
  • in that case set it to 0.0001, but its better if you set it to 0.00001. Try to go for one additional decimal place. Still, it is worth it to use double instead of single. the performance tradeoff is small, and precision increases a lot. – Eugenio De Hoyos Mar 17 '11 at 03:54
1

If you debug the app you will see the following:

1.1f - 1.0f = 0.100000024

0.1 can't be represented in binary exactly. It would be like trying to write out 1/3 in base 10 exactly, you cant do it because it goes on forever. There is another similar question that explains this and links to some code to print out what the actual value of the float is

Community
  • 1
  • 1
BrandonAGr
  • 5,827
  • 5
  • 47
  • 72
0

You are probably seeing the problem because you are using floats. Floats by their nature are not precise. Unless you need to use float for some reason, I would change my class to use Decimals instead.

Hope this helps.

Brent Stewart
  • 1,830
  • 14
  • 21