Unexpected results after optimizing switch case in Visual Studio with C#8.0
This highlights the difference between a statement and an expression. The switch you had before, was a switch statement and this was the assignment that got run.
value = BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0);
Here you are converting an uint
(right hand side) to a double
(left hand side). You were actually doing a different conversion in each branch of your switch statement, and this was fine, because well, they are separate assignment statements.
Compare that to what you are doing after the optimisation: the switch statement became a switch expression. And expressions have a single type. What is type of this expression?
stateExample switch
{
State.ExampleA => BitConverter.ToSingle(BitConverter.GetBytes((long)2147483646), 0), //Commenting this line results in correct value
State.ExampleB => BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0),
State.ExampleC => BitConverter.ToInt16(BitConverter.GetBytes((long)2147483648), 0),
_ => throw new InvalidOperationException()
}
Each branch of the switch returns a different type - float
, uint
and short
respectively. So C# needs to find a type to which all three of these can be implicitly converted. And float
is found. C# can't just "figure out" what the switch returns at runtime and work out the conversion to perform "dynamically".
The things returned in each branch has to be first converted to a float
. Therefore, the type of the whole expression is float
. Finally, you assign the float
to value
, which is a double
.
So the overall conversion is uint
-> float
-> double
, causing a loss of precision.
This will work:
double value1 = stateExample switch
{
State.ExampleA => (double)BitConverter.ToSingle(BitConverter.GetBytes((long)2147483646), 0), //Commenting this line results in correct value
State.ExampleB => BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0),
State.ExampleC => BitConverter.ToInt16(BitConverter.GetBytes((long)2147483648), 0),
_ => throw new InvalidOperationException()
};
BitConverter.ToSingle
returns float
so compiler infers float
(between float
, uint
and short
) as output type for the switch expression (and casting uint
and short
to it) and then casts it's result to double
which results in precision loss for ExampleB
case.