It all started with a trick question that someone posed to me.. (It\'s mentioned in the book - C# in a nutshell) Here\'s the gist of it.
Double a = Double.NaN;
C
The decompilation that you are seeing from Reflector is actually a bug in Reflector. Reflector needs to be able to decompile a function where two doubles are being compared; in those functions, you would find ceq
emitted right into the code. As a result, Reflector interprets a ceq
instruction as == between two doubles to help decompile a function where two doubles are being compared.
By default, value types don't come with an == implementation. (Don't user-defined structs inherit an overloaded == operator?) However, all of the built-in scalar types have an explicitly overloaded operator that the compiler translates into the appropriate CIL. The overload also contains a simple ceq
based comparison, so that dynamic/late-bound/Reflection-based invokes of the == operator overload won't fail.
For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise. For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings.
-- http://msdn.microsoft.com/en-us/library/53k8ybth.aspx
What you said implies that == uses reference type semantics for comparison of a double
. However, since double
is a value type, it uses value semantics. This is why 3 == 3
is true, even though they're different stack objects.
You can almost think of this compiler translation as how LINQ's Queryable object contains extension methods with code in them, but the compiler translates these calls into expression trees which are passed to the LINQ provider instead. In both cases, the underlying function never really gets called.
The documentation for Double does allude to how the ceq
CIL instruction works:
If two Double.NaN values are tested for equality by calling the Equals method, the method returns true. However, if two NaN values are tested for equality by using the equality operator, the operator returns false. When you want to determine whether the value of a Double is not a number (NaN), an alternative is to call the IsNaN method.
-- http://msdn.microsoft.com/en-us/library/ya2zha7s.aspx
If you look in the decompiled C# compiler source, you'll find the following code to handle direct translation of double comparisons into ceq
:
private void EmitBinaryCondOperator(BoundBinaryOperator binOp, bool sense)
{
int num;
ConstantValue constantValue;
bool flag = sense;
BinaryOperatorKind kind = binOp.OperatorKind.OperatorWithLogical();
if (kind <= BinaryOperatorKind.GreaterThanOrEqual)
{
switch (kind)
{
...
case BinaryOperatorKind.Equal:
goto Label_0127;
...
}
}
...
Label_0127:
constantValue = binOp.Left.ConstantValue;
if (((constantValue != null) && constantValue.IsPrimitiveZeroOrNull) && !constantValue.IsFloating)
{
...
return;
}
constantValue = binOp.Right.ConstantValue;
if (((constantValue != null) && constantValue.IsPrimitiveZeroOrNull) && !constantValue.IsFloating)
{
...
return;
}
this.EmitBinaryCondOperatorHelper(ILOpCode.Ceq, binOp.Left, binOp.Right, sense);
return;
}
The above code is from Roslyn.Compilers.CSharp.CodeGen.CodeGenerator.EmitBinaryCondOperator(...)
, and I added the "..."'s in order to make the code more readable for this purpose.
In msdn it is stated that;
If two Double.NaN values are tested for equality by calling the Equals method, the method returns true. However, if two NaN values are tested for equality by using the equality operator, the operator returns false. When you want to determine whether the value of a Double is not a number (NaN), an alternative is to call the IsNaN method.
This is done delibaretly to conform with IEC 60559:1989 since it states that two NaN values are not equal as they are not treated as numbers, so op_Equal
definition conforms with this standart;
According to IEC 60559:1989, two floating point numbers with values of NaN are never equal.However, according to the specification for the System.Object::Equals method, it's desirable to override this method to provide value equality semantics. Since System.ValueType provides this functionality through the use of Reflection, the description for Object.Equals specifically says that value types should consider overriding the default ValueType implementation to gain a performance increase. In fact from looking at the source of System.ValueType::Equals (line 36 of clr\src\BCL\System\ValueType.cs in the SSCLI), there's even a comment from the CLR Perf team to the effect of System.ValueType::Equals not being fast.
refer to: http://blogs.msdn.com/b/shawnfa/archive/2004/07/19/187792.aspx
From msdn : http://msdn.microsoft.com/en-us/library/ya2zha7s.aspx
If two Double.NaN values are tested for equality by calling the Equals method, the method returns true. However, if two NaN values are tested for equality by using the equality operator, the operator returns false. When you want to determine whether the value of a Double is not a number (NaN), an alternative is to call the IsNaN method.