barlop
barlop

Reputation: 13820

Why is == overridden in System.Double but not in System.Int32 and what are the ramifications of that?

In C# Why does Double override == but Int32 does not, and what is the effect?

I look at the msdn library.

I see this link about double which doesn't say much here (though I understand double is a shorthand for the Double object).. It doesn't show methods for example..

but this link on System.Double does mention what I'm looking for here

It shows the Equality operator taking doubles, so overloaded.

Image For Double in MSDN, then after Methods (before listing Fields), it shows Operators, and it shows equality operator is overridden

enter image description here

and I can click Equality under "Operators" and it says

public static bool operator ==(
    double left,
    double right
)

Whereas when I look at System.Int32 Image For Int32 in MSDN is below

enter image description here

See in that image, (the page for System.Int32) it looks like == is not overridden.

Why, and what are the ramifications of this?

Upvotes: 10

Views: 481

Answers (3)

TyCobb
TyCobb

Reputation: 9089

Int32 seems to be very special in terms of .NET. The functionality that is missing from the source code is more than likely baked into core of the system.

You cannot compare structs/value-types with ==, >, etc. without declaring those operators inside the struct. Because Int32 is missing these I came to the conclusion above.

Doing a simple test and dumping the IL, they are doing the exact same comparison and no CompareTo or Equals is getting called (which I thought actually happened. I learned something!).

public void TestInts()
{
    var x = 1;
    var y = 2;
    var equals = x == y;
}

.method public hidebysig 
    instance void TestInts () cil managed 
{
    // Method begins at RVA 0x2094
    // Code size 11 (0xb)
    .maxstack 2
    .locals init (
        [0] int32 x,
        [1] int32 y,
        [2] bool equals
    )

    IL_0000: nop
    IL_0001: ldc.i4.1
    IL_0002: stloc.0
    IL_0003: ldc.i4.2
    IL_0004: stloc.1
    IL_0005: ldloc.0
    IL_0006: ldloc.1
    IL_0007: ceq
    IL_0009: stloc.2
    IL_000a: ret
}

public void TestDoubles()
{
    var x = 1.7d;
    var y = 1.5d;
    var equals = x == y;
}
.method public hidebysig 
    instance void TestDoubles () cil managed 
{
    // Method begins at RVA 0x20ac
    // Code size 27 (0x1b)
    .maxstack 2
    .locals init (
        [0] float64 x,
        [1] float64 y,
        [2] bool equals
    )

    IL_0000: nop
    IL_0001: ldc.r8 1.7
    IL_000a: stloc.0
    IL_000b: ldc.r8 1.5
    IL_0014: stloc.1
    IL_0015: ldloc.0
    IL_0016: ldloc.1
    IL_0017: ceq
    IL_0019: stloc.2
    IL_001a: ret
}

The above IL just has the standard ceq opcode called for both cases. By .NET standards, Int32 should have the comparison operators declared in the source code, but it does not.

EDIT: It appears as though all whole-number value-types are like this. Single, Double, Decimal all have the operators specified in the source code. Int16, Int32, Int64, Byte, do not.

Upvotes: 1

Matt
Matt

Reputation: 6050

One possible reason is because of the Double.NaN.

For the == operator: MSDN says: If two Double.NaN values are tested for equality by using the equality operator(==), the result is false; two Double.NaN values are not considered equal. If they are tested for equality by calling the Equals method, the result is true. When you want to determine whether the value of a Double is not a number (NaN), an alternative is to call the IsNaN method.

So the == operator and the Equals methods of Double have different behavior regards to Double.NaN, I think this why == is override for double. As for int, there's no such special case.

The code to demo the differences:

using System;

public class Example
{
   public static void Main()
   {
      Console.WriteLine("NaN == NaN: {0}", Double.NaN == Double.NaN); 
      Console.WriteLine("NaN != NaN: {0}", Double.NaN != Double.NaN); 
      Console.WriteLine("NaN.Equals(NaN): {0}", Double.NaN.Equals(Double.NaN)); 
      Console.WriteLine("! NaN.Equals(NaN): {0}", ! Double.NaN.Equals(Double.NaN)); 
      Console.WriteLine("IsNaN: {0}", Double.IsNaN(Double.NaN));

      Console.WriteLine("\nNaN > NaN: {0}", Double.NaN > Double.NaN); 
      Console.WriteLine("NaN >= NaN: {0}", Double.NaN >= Double.NaN); 
      Console.WriteLine("NaN < NaN: {0}", Double.NaN < Double.NaN);
      Console.WriteLine("NaN < 100.0: {0}", Double.NaN < 100.0); 
      Console.WriteLine("NaN <= 100.0: {0}", Double.NaN <= 100.0); 
      Console.WriteLine("NaN >= 100.0: {0}", Double.NaN > 100.0);
      Console.WriteLine("NaN.CompareTo(NaN): {0}", Double.NaN.CompareTo(Double.NaN)); 
      Console.WriteLine("NaN.CompareTo(100.0): {0}", Double.NaN.CompareTo(100.0)); 
      Console.WriteLine("(100.0).CompareTo(Double.NaN): {0}", (100.0).CompareTo(Double.NaN)); 
   }
}
// The example displays the following output: 
//       NaN == NaN: False 
//       NaN != NaN: True 
//       NaN.Equals(NaN): True 
//       ! NaN.Equals(NaN): False 
//       IsNaN: True 
//        
//       NaN > NaN: False 
//       NaN >= NaN: False 
//       NaN < NaN: False 
//       NaN < 100.0: False 
//       NaN <= 100.0: False 
//       NaN >= 100.0: False 
//       NaN.CompareTo(NaN): 0 
//       NaN.CompareTo(100.0): -1 
//       (100.0).CompareTo(Double.NaN): 1

The code is also from MSDN

Upvotes: 3

Artfunkel
Artfunkel

Reputation: 1852

Using ILSpy I can see that Double.Equals has some additional logic to check whether either value being compared is NaN.

ILSpy also decompiles the body of == to this:

public static bool operator ==(double left, double right)
{
    return left == right;
}

The other operators follow the same pattern, which is weird. Perhaps a decompile error?

Upvotes: -1

Related Questions