NCalc specify type
Asked Answered
A

2

6

We have a generic calculation routine using Ncalc to evaluate a string. However we are running across a problem when the values in a multiplication are small enough that Ncalc sees them as int however the result is too big for an int.

Example:

        var expr = new NCalc.Expression("1740263 * 1234");
        object result = expr.Evaluate();
        Console.WriteLine(result.ToString());
        Console.ReadKey();

This results in a negative int value.

Is there any way to force Ncalc to use long for the calculation?

I have tried using parameters and this works but it would mean a major rewrite of our code to implement and I would like to avoid it if possible.

Thanks

Advancement answered 30/3, 2016 at 8:14 Comment(0)
C
4

NCalc uses Int32 as integer data type then you can't force a number to be calculated as Int64. However if you don't use built-in math functions and you rely on plain mathematical operators you may convert a number to long and it will invoke correct operators. Let's see it:

var expr = new NCalc.Expression("CLng(1740263) * 1234");
expr.EvaluateFunction += delegate(string name, NCalc.FunctionArgs args)
{
    // Nostalgic CLng function...
    if (String.Equals(name, "CLng", StringComparison.CurrentCultureIgnoreCase))
    {
        args.HasResult = true;
        args.Result = Convert.ToInt64(args.EvaluateParameters()[0]);
    }
};

Please note that you can't directly cast boxed Int32 parameter to Int64 then you have to use Convert.ToInt64() or double cast: (long)(int)args.EvaluateParameters()[0]. Now you can check result is correct:

var result = expr.Evaluate();
Console.WriteLine("Result is: {0} (type {1})",
    result, result.GetType());

Proper type conversion is performed then you don't need to cast both values to long.

Note that you may also directly use floating point numbers (decimal in NCalc) and you won't have such problems:

var expr = new NCalc.Expression("1740263.0 * 1234.0");
Citation answered 30/3, 2016 at 8:35 Comment(1)
Thanks for your detailed response. It has helped a lot.Advancement
R
0

You can use regex to extract all the number from the string, replace them with parameters and then set the parameters to the converted numbers. In this example I am replacing what would of been int numbers to decimal.

string myCalculationString = "1000000 * 10000000";
striny myPattern = @"-?\d+(,\d+)*(\.\d+(e\d+)?)?";

MatchCollection matches =
Regex.Matches(convertedCalculation, myPattern );

int matchCount = 0;
convertedCalculation = Regex.Replace(convertedCalculation,
myPattern , m => "[p" + matchCount++ + "]");

for (int i = 0; i < matches.Count; i++)
    {
       Match match = matches[i];
       decimal number = Convert.ToDecimal(match.Value);
       expression.Parameters.Add($"p{i}", number);
    }

 decimal result = (decimal)expression.Evaluate();

Note I have not tested the regular expression well but it seems to work for positive/negative numbers and allows optional decimal point. This will also throw an error if any of the numbers or the result is out of bounds for a decimal

Reflective answered 1/7, 2016 at 9:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.