Search code examples
c#.netcilsystem.reflection

Why (int)==(float) always compiles to (float)==(float)


I'm researching C# compilers and trying to understand the math operation rules.

I found a incomprehensible behavior with == operator between two different primitive types.

int a = 1;
float b = 1.0f;        
Console.WriteLine(a == b);

This actually compiles to

.locals init (
    [0] int32,
    [1] float32
)

IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: ldc.r4 1
IL_0008: stloc.1
IL_0009: ldloc.0
IL_000a: conv.r4
IL_000b: ldloc.1
IL_000c: ceq

which means

(float)a == (float)b

My expectation was (int)a == (int)b because left value is an integer.

Any reasons for this result?


  • Here's my guess: int->float is faster than float->int

Solution

  • This is really nothing to do with speed (as you suggest) and more to do with Implicit Conversions, you can find the relevant information defined under the topic of Numeric Promotions in the C# Specifications

    12.4.7 Numeric promotions

    Numeric promotion consists of automatically performing certain implicit conversions of the operands of the predefined unary and binary numeric operators. Numeric promotion is not a distinct mechanism, but rather an effect of applying overload resolution to the predefined operators. Numeric promotion specifically does not affect evaluation of user-defined operators, although user-defined operators can be implemented to exhibit similar effects.

    As an example of numeric promotion, consider the predefined implementations of the binary * operator:

    int operator *(int x, int y);
    uint operator *(uint x, uint y);
    long operator *(long x, long y);
    ulong operator *(ulong x, ulong y);
    float operator *(float x, float y);
    double operator *(double x, double y);
    decimal operator *(decimal x, decimal y);
    

    When overload resolution rules (§12.6.4) are applied to this set of operators, the effect is to select the first of the operators for which implicit conversions exist from the operand types.

    Further More

    Binary numeric promotion occurs for the operands of the predefined +, , *, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. Binary numeric promotion consists of applying the following rules, in the order they appear here:

    • If either operand is of type decimal, the other operand is converted to type decimal, or a bindingtime error occurs if the other operand is of type float or double.
    • Otherwise, if either operand is of type double, the other operand is converted to type double.
    • Otherwise, if either operand is of type float, the other operand is converted to type float.
    • Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a binding-time error occurs if the other operand is of type sbyte, short, int, or long.
    • Otherwise, if either operand is of type long, the other operand is converted to type long.
    • Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
    • Otherwise, if either operand is of type uint, the other operand is converted to type uint.
    • Otherwise, both operands are converted to type int.

    You can get a feel for this with the examples they show

    byte b = 1;
    short a = 2;
    WriteLine((int)b==(int)s); // promotes both to int
    
    int i = 1;
    double d = 2;
    WriteLine((double)i==d); // promotes i to double
    

    Or your example

    int a = 1;
    float b = 1.0f; 
    WriteLine((float)a==b); // promotes a to float