#415 – Be Careful When Checking Floating Point Numbers for Equality

When you check two floating point numbers for equality in C#, you might sometimes be surprised at the result.

Consider the following example.  f2 is equal to six sixths (1.0), minus 1.0–which should be equal to 0.0.  But when we compare the result to 0.0, we see that the values are not equal.

            float f1 = 1.0f / 6.0f;
            float f2 = (f1 * 6.0f) - 1.0f;   // Should be 0.0
            float f3 = 0.0f;

            bool check = (f2 == f3);    // Should be true, but is false !

This happens because floating point numbers can’t necessarily be represented exactly when stored in a floating point variable.  Instead, the value stored can be very close, but not equal to, the desired value.

Here’s the output from the example above.  f2 should be equal to 0.0, after our calculation.  It is instead very nearly equal to 0.0.  Because of this, the comparison to the constant 0.0 fails.

Advertisement

#405 – Equals Method for Equivalence, == Operator for Identity

You can check for equality between two objects by calling the Equals method or by using the == operator.  There are some differences between how the methods work, but both Equals and == can be overloaded in a user-defined type.

You can therefore change the behavior of one of these methods, or both.  The question is–how should these two behaviors work for a custom class?

Roughly speaking, the desired behavior, from a client’s point of view is:

  • Use Equals method to determine value equality, or equivalence–do the two objects represent the same thing?
  • Use == operator to determine reference equality, or identity–do the two references point to exactly the same object?

This argues for overloading Equals for a user-defined reference type, but not overloading the == operator.

This would result in the following behavior:

            Dog d1 = new Dog("Lassie", 7);
            Dog d2 = new Dog("Lassie", 7);

            bool bValueEquality = d1.Equals(d2);    // true
            bool bRefEquality = d1 == d2;    // false

#404 – Equals Method vs. == Operator for Value Types

In C#, you can compare two objects for equality by using the Equality operator (==) or by calling the Equals method.  For built-in value types, the default behavior checks for value equality, or equivalence.

            int i1 = 42;
            int i2 = 42;

            bool check1 = i1 == i2;      // true
            bool check2 = i1.Equals(i2);  // true
            bool check3 = int.Equals(i2, i2);  // true

For the predefined value types (e.g. int, float):

  • The Equals method performs a value equality check
  • The == operator resolves to the CIL ceq instruction, which does a strict identity check

For user-defined struct types:

  • The Equals method implementation performs a value equality check by comparing each field of the struct (using reflection).  You can (and should) override Equals to provide an implementation specific to the struct.
  • The == operator is undefined, unless you override it in the struct.

#403 – Equals Method vs. == Operator for Reference Types

In C#, you can compare two objects for equality by using the Equality operator (==) or by calling the Equals method.  In both cases, the default behavior for reference types is to check for reference equality, or identity.

    bool sameDog = yourDog.Equals(myDog);
    bool sameDog2 = yourDog == myDog;
    bool sameDog3 = Dog.Equals(yourDog, myDog);

By default, for a reference type, these mechanisms behave exactly the same way, implementing a reference equality check.  Under the covers, they are a bit different:

  • The Equals method is an instance method of the System.Object class.  It does some checks for null, but then uses the == operator in the default implementation.  It is a virtual method that you can override.
  • The == operator resolves to the CIL ceq instruction, which does a strict identity check.  You can override the == operator, in which case a new method named op_Equality is defined/called.

You can override Equals and the == operator independently.

#402 – Value Equality vs. Reference Equality

When we normally think of “equality”, we’re thinking of value equality–the idea that the values stored in two different objects are the same.  This is also known as equivalence.  For example, if we have two different variables that both store an integer value of 12, we say that the variables are equal.

            int i1 = 12;
            int i2 = 12;

            // Value equality - evaluates to true
            bool b2 = (i1 == i2);

The variables are considered “equal”, even though we have two different copies of the integer value of 12.

We can also talk about reference equality, or identity–the idea that two variables refer to exactly the same object in memory.

            Dog d1 = new Dog("Kirby", 15);
            Dog d2 = new Dog("Kirby", 15);
            Dog d3 = d1;

            bool b1 = (d1 == d2);   // Evaluates to false
            bool b2 = (d1 == d3);   // Evaluates to true


In C#, the == operator defaults to using value equality for value types and reference equality for reference types.

#86 – Equality and Inequality Operators

In C#, the equality and inequality operators are used to check the equivalence of two operands.  The operands can be of any type.

  • ==    Equality
  • !=      Inequality

These operators always take two operands and return a boolean value.

  • == Returns true if its operands are equal
  • != Returns true if its operands are not equal

The behavior of the operators depends on the type:

  • Value types – equal if values are equal
  • Reference types – equal if the operands point to the same object
  • string type – equal if the values are equal
  • Custom types – you can override the behavior

Examples:

// Value types
 bool b1 = (1 == 2);     // false
 int i = 12, j = 12;
 b1 = (i == j);     // true
 b1 = (i != j);     // false

 // Reference types
 Cat c1 = new Cat("Herman");
 Cat c2 = new Cat("Boris");
 Cat c3 = new Cat("Herman");
 Cat c4 = c1;
 b1 = (c1 == c2);    // false
 b1 = (c1 == c3);    // false, though same name
 b1 = (c4 == c1);    // true, both point to the same Cat

#67 – Default Behavior for Reference Type Equality

For reference types, a default equality check normally checks to see if the references are pointing to the exact same object–rather than checking to see if the objects pointed to are equivalent.

Suppose we have a Person type that has a constructor that takes Name and Age values.

 public class Person
 {
     public string Name { get; set; }
     public uint Age { get; set; }

     public Person(string name, uint age)
     {
         Name = name;
         Age = age;
     }
  }

Now suppose that we create two instances of the Person object with the same values for Name and Age.  The code fragment below shows what happens when we check to see if the resulting objects are equal.

 Person p1 = new Person("Sean", 46);
 Person p2 = new Person("Sean", 46);

 bool b = (p1 == p2);    // False, because p1 and p2 point to different objects