People often have questions about the consistency of floating point calculations. There are currently almost no guarantees provided by the .NET Framework. To quote Eric Lippert :
The C # compiler, jitter, and runtime are wide in order to give you more accurate results than what is required in the specification, at any time on a whim - they donβt need to choose to do this consistently and in fact that they do not.
In this particular case, the answer is simple. Untreated IL for release:
IL_0000: ldc.r4 0.333333343 IL_0005: conv.r8 IL_0006: ldc.r8 0.33333333333333331 IL_000f: stloc.0 IL_0010: ldc.r8 0.33333333333333331 IL_0019: stloc.1 IL_001a: call void [mscorlib]System.Console::WriteLine(float64) IL_001f: ldloc.0 IL_0020: call void [mscorlib]System.Console::WriteLine(float64) IL_0025: ldloc.1 IL_0026: call void [mscorlib]System.Console::WriteLine(float64) IL_002b: ret
All arithmetic here is performed by the compiler. In the Roslyn compiler, the fact that temp_t1 is a variable causes the compiler to emit an IL that loads a 4-byte float and then converts it to double. I believe this is consistent with previous versions. In two other cases, the compiler performs all arithmetic operations with double precision and saves these results. It is not surprising that the second and third cases do not differ from each other, since the compiler does not store local constants in IL.
source share