Reputation: 15
I have C# parser and it parses 15(Double) into 15, but CIL does the same to 15,000000000000004
.method private static valuetype Hw2.CalculatorOperation ParseOperation(string arg) cil managed
{
ldarg.0
ldstr "+"
call bool [System.Runtime]System.String::op_Equality(string, string)
brtrue.s PlusCase
ldarg.0
ldstr "-"
call bool [System.Runtime]System.String::op_Equality(string, string)
brtrue.s MinusCase
ldarg.0
ldstr "/"
call bool [System.Runtime]System.String::op_Equality(string, string)
brtrue.s DivideCase
ldarg.0
ldstr "*"
call bool [System.Runtime]System.String::op_Equality(string, string)
brtrue.s MultiplyCase
ldc.i4.4
ret
PlusCase:
ldc.i4.0
ret
MinusCase:
ldc.i4.1
ret
MultiplyCase:
ldc.i4.2
ret
DivideCase:
ldc.i4.3
ret
}
.method private static bool IsArgLengthSupported(string[] args) cil managed
{
ldarg.0
ldlen
ldc.i4.3
ceq
ret
}
.method public static void ParseCalcArguments(string[] args,
[out] float64& val1,
[out] valuetype Hw2.CalculatorOperation& op,
[out] float64& val2) cil managed
{
ldarg.0
call bool Hw2.Parser::IsArgLengthSupported(string[])
ldc.i4.0
beq.s ArgumentException
ldarg.0
ldc.i4.1
ldelem.ref
call valuetype Hw2.CalculatorOperation Hw2.Parser::ParseOperation(string)
ldc.i4.4
ceq
brtrue InvalidOperationException
ldarg.0
ldc.i4.0
ldelem.ref
ldarg.1
call bool [System.Runtime]System.Double::TryParse(string, float64&)
brtrue.s SecondTryParse
ldstr "first number isn't actually a number"
newobj instance void [System.Runtime]System.ArgumentException::.ctor(string)
throw
SecondTryParse:
ldarg.0
ldc.i4.2
ldelem.ref
ldarg.3
call bool [System.Runtime]System.Double::TryParse(string, float64&)
brtrue.s CalculationOperationDefining
ldstr "second number isn't actually a number"
newobj instance void [System.Runtime]System.ArgumentException::.ctor(string)
throw
CalculationOperationDefining:
ldarg.1
ldarg.0
ldc.i4.1
ldelem.ref
call valuetype Hw2.CalculatorOperation Hw2.Parser::ParseOperation(string)
stind.i4
ret
InvalidOperationException:
ldstr "unknown operation"
newobj instance void [System.Runtime]System.InvalidOperationException::.ctor(string)
throw
ArgumentException:
ldstr "array length should be 3"
newobj instance void [System.Runtime]System.ArgumentException::.ctor(string)
throw
}
and my test
[InlineData("*", CalculatorOperation.Multiply)]
public void TestCorrectOperations(string operation, CalculatorOperation operationExpected)
{
// arrange
var args = new[] { "15", operation, "5" };
//act
Parser.ParseCalcArguments(args, out var val1, out var operationResult, out var val2);
//assert
Assert.Equal(15, val1);
Assert.Equal(operationExpected, operationResult);
Assert.Equal(5, val2);
}
C# Parser code
public static void ParseCalcArguments(string[] args,
out double val1,
out CalculatorOperation operation,
out double val2)
{
if (!IsArgLengthSupported(args))
throw new ArgumentException("array length should be 3");
if (ParseOperation(args[1]) == CalculatorOperation.Undefined)
throw new InvalidOperationException();
if (Double.TryParse(args[0], out val1) && Double.TryParse(args[2], out val2))
{
operation = ParseOperation(args[1]);
}
else
throw new ArgumentException("one of the numbers isn't actually a number");
}
Makes me face
Expected: 15, Actual: 15,000000000000004 in "*" operation test. Interesting, but "+" operation test passes, unlike "-" and "/".
My guess is that C#, when it's being converted into CIL, writes some code to check if converted number is almost 15(big number of digits behind floating point), but CIL doesn't as it's being turned into machinery code without any convertions. Any explanations?
Initially i started to make round method myself but decided it's better to ask first.
Upvotes: 0
Views: 79