简体   繁体   中英

C# Equals() == about number

I try following code use == and Equals on number comparison:

Console.WriteLine( (int)2 == (double)2.0 );     
Console.WriteLine( ( (int)2 ).Equals( (double)2.0) );

Console.WriteLine((float)2.0 == (double)2.0);
Console.WriteLine( ( (float)2.0 ).Equals( (double)2.0 )   );

The result:

true 
false
true 
false

int, double, float are all ValueType , after reading posts Here1 and Here2 , I still cannot understand why == and Equals turns out different result,

What is the working detail behind == and Equals in these 4 cases about number?

(if this question is duplicate please tell me)



EDIT: 4 more interesting cases:

double, float <-> int

Console.WriteLine((double)2.0 == (int)2);              //True
Console.WriteLine(((double)2.0).Equals((int)2));       //True

Console.WriteLine((float)2.0 == (int)2.0);             //True
Console.WriteLine(((float)2.0).Equals((int)2.0));      //True

double, int <-> float

Console.WriteLine((double)2.0 == (float)2.0);          //True
Console.WriteLine(((double)2.0).Equals((float)2.0));   //True

Console.WriteLine((int)2 == (float)2.0);               //True
Console.WriteLine(((int)2).Equals((float)2.0));        //False

From MSDN :

ValueType.Equals indicates whether this instance and a specified object are equal.

and

Return value:

Type: System.Boolean

true if obj and this instance are the same type and represent the same value ; otherwise, false.*

If you do this:

        int a = 1;
        double b = a;
        bool check = a.Equals(b);

You are calling this implementation of Equals:

[__DynamicallyInvokable]
public override bool Equals(object obj)
{
  if (!(obj is int))
    return false;
  return this == (int) obj;
}

If you do this:

        int a = 1;
        int b = a;
        bool check = a.Equals(b);

You are calling this other:

[NonVersionable]
[__DynamicallyInvokable]
public bool Equals(int obj)
{
  return this == obj;
}

(int)2 == (double)2.0 and (float)2.0 == (double)2.0 are compared at compile time. Actually it doesn't compare the backing data types but the values as seen by the compiler (hence 2==2 ). And even then, the == on float / int does an implicit type conversion.

The Equals method though is ran on runtime, where types are different and hence the method returns false .

(int)2 == (double)2.0            - True because the compiler promotes int to double when comparing via ==.
((int)2).Equals( (double)2.0)    - False because this is calling int.Equals(object) and the types are different.
(float)2.0 == (double)2.0        - True because the compiler promotes float to double when comparing via ==.
((float)2.0).Equals((double)2.0) - False becaue this is calling float.Equals(object) and the types are different.
(double)2.0 == (int)2            - True because the compiler promotes int to double when comparing via ==.
((double)2.0).Equals((int)2)     - True because there exists double.Equals(double) and the compiler
                                   promotes the integer parameter 2 to double to call double.Equals(2.0).
(float)2.0 == (int)2.0           - True because the compiler promotes int to float when comparing via ==.
((float)2.0).Equals((int)2.0)    - True because there exists float.Equals(float) and the compiler
                                   promotes the integer parameter 2 to float to call float.Equals(2.0f).
(double)2.0 == (float)2.0)       - True because the compiler promotes float to double when comparing via ==.
((double)2.0).Equals((float)2.0) - True because there exists double.Equals(double) and the compiler
                                   promotes the float parameter 2.0f to double to call double.Equals(2.0).
(int)2 == (float)2.0             - True because the compiler promotes int to float when comparing via ==.
((int)2).Equals((float)2.0)      - False because this is calling int.Equals(object) and the types are different.

Note that in the cases where false is returned, it's because although int.Equals(int) exists, the compiler cannot call it because there is no automatic type conversion from floating point types to int .

The == is an operator and the compiler will first apply implicit conversions to widen one of the the operands when needed.

1.0 == 1 => 1.0 == 1.0 => true

The Equals() method does not trigger implicit conversion and so it returns false. It is also more expensive, requiring a boxing operation. And one of the first things it checks is if the operands are of the same type.

(1.0).Equals(1) => Double(1.0).Equals(object(1)) => Double == Int32 => false

As plain numbers, == compares the values. When using the .Equals method, they are treated as instances of object and hence the .Equals sees the types as different and the condition fails.

You would expect it to give true, but the type checking condition precedes the value comparison.

Thanks for everyone's answer, they are all good informations, and make me find Equals method Doc of each type.

In all cases related to == are all true, and cases related to Equals are:

Console.WriteLine( ( (int)2 ).Equals( (double)2.0) );      //False
Console.WriteLine( ( (float)2.0 ).Equals( (double)2.0 ) ); //False
Console.WriteLine(((double)2.0).Equals((int)2));           //True
Console.WriteLine(((float)2.0).Equals((int)2.0));          //True
Console.WriteLine(((double)2.0).Equals((float)2.0));       //True
Console.WriteLine(((int)2).Equals((float)2.0));            //False

and equals method MSDN link: Double , Int32 , Float(Single)

In method description, Equals method are all in the format:

public bool Equals(double obj)
public bool Equals(int obj)
public bool Equals(float obj)

So I guess when using Equals, the value wait to be compared will be first converted, and key point is whether the type can be converted without being truncated or rounded off.

Here correspond to 6 cases:

// double cannot implicit convert to int -> F
Console.WriteLine( ( (int)2 ).Equals( (double)2.0) );      //False
// double cannot implicit convert to float -> F
Console.WriteLine( ( (float)2.0 ).Equals( (double)2.0 ) ); //False
// int can implicit convert to double -> T
Console.WriteLine(((double)2.0).Equals((int)2));           //True
// int can implicit convert to float -> T
Console.WriteLine(((float)2.0).Equals((int)2.0));          //True
// float can implicit convert to double -> T
Console.WriteLine(((double)2.0).Equals((float)2.0));       //True
// float cannot implicit convert to int -> F
Console.WriteLine(((int)2).Equals((float)2.0));            //False

Edit and Correct:

By Go to Definition Funciton in VS, True Case all go to Equals(double/float/int obj) , and the False Case all go to Equals(object obj) , and in the description of Equals(object obj) :

Returns:
true if obj <is an instance of Int/Float/Double> and <equals the value> of this instance; 
otherwise, false.

So I guess if the implicit conversion fail, it goes to Equals(object obj) and get false when type checking.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM