Nathan Ikazuyir
Nathan Ikazuyir

Reputation: 15

CIL code parses "15" into 15,000000000000004

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

Answers (0)

Related Questions