Mathematical expression in string: Alternatives to NCalc - c#

I need to read from file mathematical expression and evaluate it's value. Example expression formats are as follow:
"5" - constant
"3.1415 * 0.25" - constant expressions
"{0} - 50" - expressions with value placeholders (String.Format())
"Abs({0} - 50)" - just like up but with mathematical functions
I was using so far NCalc which worked great until it had to deal with expressions like follow:
"3.0 * Abs({0} + 34)"
Unfortunately in example just above the result of following code:
var value = ReadValueFromSomewhere(); // Lets say it returns 125.75
var exprStr = ReadExpression(); // returns: "3.0 * Abs({0} + 34)"
var toEval = String.Format(exprStr, value);
var result = new NCCalc.Expression(toEval).Evaluate()
is following exception:
System.InvalidOperationException
Operator '*' can't be applied to operands of types 'double' and 'decimal'
NCalc.Numbers.Multiply(object, object)
NCalc.Domain.EvaluationVisitor.Visit(NCalc.Domain.BinaryExpression)
NCalc.Domain.BinaryExpression.Accept(NCalc.Domain.LogicalExpressionVisitor)
NCalc.Expression.Evaluate()
It seems like Abs() method returns decimal and NCalc can't handle doing calculations between double and decimal (propably bug?). So I would like to ask what alternative libraries I could use instead of NCalc? Or perhaps there is other workaround than expression:
"Abs(3.0) * Abs({0} + 34)"
?

What do you mean by "expressions with value placeholders"? Can you give more specific example? You can try mXparser - it works for Java and .NET.
Example of usage:
Expression e = new Expression("3.1415 * 0.25");
double v = e.calculate();
Follow mXparser tutorial.
Regards

Related

How does the C# compiler resolve types before applying a binary operator?

I'm working on a typed scripting language backed by C# Expression Trees. I'm stuck on one issue around proper type conversion with binary operators. Here is an example of the behavior I'm trying to mimic: (The conversion rules should be the same C#'s compiler)
var value = "someString" + 10; // yields a string
var value = 5 + "someString"; // also yields a string, I don't know why
var x = 10f + 10; // yields a float
var y = 10 + 10f; // also yields a float, I don't know why
How does the C# compiler know to call ToString() on the integer in the first line and to convert the integers to floats in both directions when adding with a float? Are these conversion rules hard coded?
My compiler basically works like this now for binary operators:
Expression Visit(Type tryToConvertTo, ASTNode node) {
// details don't matter. If tryToConvertTo is not null
// the resulting expression is cast to that type if not already of that type
}
// very simplified but this is the gist of it
Expression VisitBinaryOperator(Operator operator) {
Expression lhs = Visit(null, operator.lhs);
Expression rhs = Visit(lhs, operator.rhs); // wrong, but mostly works unless we hit one of the example cases or something similar
switch(operator.opType) {
case OperatorType.Add: {
return Expression.Add(lhs, rhs);
}
// other operators / error handling etc omitted
}
}
I know always accepting the left hand side's type is wrong, but I have no idea what the proper approach to resolving the example expressions might be other than hard coding the rules for primitive types.
If anyone can point me in the right direction I'd be very grateful!
This kind of questions can only be answered accurately via the language specification.
+ operator with string and int operands
https://github.com/dotnet/csharpstandard/blob/draft-v7/standard/expressions.md#1195-addition-operator
Here, under String concatenation you will see:
These overloads of the binary + operator perform string concatenation. If an operand of string concatenation is null, an empty string is substituted. Otherwise, any non-string operand is converted to its string representation by invoking the virtual ToString method inherited from type object. If ToString returns null, an empty string is substituted.
+ operator with float and int operands
The int here is implicitly converted to float, specified here:
https://github.com/dotnet/csharpstandard/blob/draft-v7/standard/conversions.md#1023-implicit-numeric-conversions

How to convert to float if there are tons of lines?

I've got decompiled source codes, they got simple errors like below:
this.SubReport.Top = 77.0 / 16.0;
Error 18 Cannot implicitly convert type 'double' to 'float'. An
explicit conversion exists (are you missing a
cast?)
The solution is simple:
this.SubReport.Top = (float)(77.0 / 16.0);
// or
this.SubReport.Top = 77.0f / 16.0f;
However there are tons of lines. What is the easiest way to clear these errors except replacing text? Because that's why I'm asking the question, I don't want to use keyboard & replace every single error, there are 1000+ lines. Doubleclicking the error & surrounding the numbers with (float) is my last resort.
You can use Find & Replace with RegEx options
Example:
Input: this.SubReport.Top = 77.0 / 16.0;
Output: this.SubReport.Top = (float)(77.0 / 16.0);
Edit:
Also you could use groups to add the float f to the values other than casting
Input: this.SubReport.Top = 77.0 / 16.0;
Output: this.SubReport.Top = 77.0f / 16.0f;
Try to help yourself and automate Find and Replace with regular expressions. For example, you can change all divides:
Then find other such patterns to replace them automatically.
Another approach is to change Top to a property with a double type. Then, inside the property you can do the casting.
float _top = 0.0;
double Top
{
set
{
_top = (float)value;
}
get
{
return _top;
}
}
You could also use something like Resharper to refactor code paths that access the getter for Top and introduce another property in its place as a floating point type that returns (float)Top in its property. Something like:
float FloatTop
{
get
{
return (float)Top;
}
}

Error 1 Invalid expression term 'double'

private void buttonConvert_Click(object sender, EventArgs e)
{
//Convert number from C to F
double convertDecimal;
convertDecimal = 1.8;
textBoxF = double.Parse(textBoxC.Text) * double(convertDecimal) + 32;
^here is where I get the error
Error 1 Invalid expression term 'double'
I am pretty new to programming and but I just can't wrap my mind around trying to add, subtract, dividing, or multiplying numbers. I am trying to do a simple a simple conversion. Take the number from the Celsius textbox and convert it to Fahrenheit. I just don't understand the number part???
Thanks for your help!
double(convertDecimal) should be (double)convertDecimal
That looks like a C++ type-casting expression, which doesn't work in C#. And as convertDecimal already is of type double there's no need to cast it. Just use it directly:
textBoxF = double.Parse(textBoxC.Text) * convertDecimal + 32;
You only need to change the type of a variable (i.e. type-cast) when the variable is of a type not expected. Adding two double values is okay. Even adding a double and an int is okay because the integer is implicitly converted to a double.
Edit: You try to assign the result of the expression to a control, which will not work. You should convert the result to a string (e.g. with double.ToString), and then assign to the controls text field:
double farenheit = double.Parse(textBoxC.Text) * convertDecimal + 32;
textBoxF.Text = farenheit.ToString();

Evaluate C# expression inside another expression

I want to use an expression in another one:
Expression<Func<double, double>> f = x => x * x * 27 + blah ... expression with x;
Expression<Func<double, double>> g = y => 3 + 8 * f.Compile()(y) * y * blah... expression with y and f(y);
This will not work when sent to LINQ to SQL because f.Compile() is unknown to SQL.
How do you evaluate the expression f on the variable y without compiling it, but still using normal syntax to define g?
I don't want to have to define all of g with some unreadable Expression.Add/Expression.Multiply etc. statements.
Thanks.
Take a look at Calling functions in LINQ queries and LINQ Extensions project. CLinq part is irrelevant to your question, but it also includes LinqExt library, which is just what are you looking for. The same approach is also used by LinqKit which also provides other useful extensions for Linq.

"The binary operator Add is not defined for the types 'System.String' and 'System.String'." -- Really?

When trying to run the following code:
Expression<Func<string, string>> stringExpression = Expression.Lambda<Func<string, string>>(
Expression.Add(
stringParam,
Expression.Constant("A")
),
new List<ParameterExpression>() { stringParam }
);
string AB = stringExpression.Compile()("B");
I get the error referenced in the title: "The binary operator Add is not defined for the types 'System.String' and 'System.String'." Is that really the case? Obviously in C# it works. Is doing string s = "A" + "B" in C# special syntactic sugar that the expression compiler doesn't have access to?
It's absolutely right, yes. There is no such operator - the C# compiler converts string + string into a call to string.Concat. (This is important, because it means that x + y + z can be converted into string.Concat(x, y, z) which avoids creating intermediate strings pointlessly.
Have a look at the docs for string operators - only == and != are defined by the framework.
This just caught me out too, and as Jon points out in his answer, the C# compiler converts string + string into string.Concat. There is an overload of the Expression.Add method that allows you to specify the "add" method to use.
var concatMethod = typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string) });
var addExpr = Expression.Add(Expression.Constant("hello "),Expression.Constant("world"), concatMethod);
You might want to change the string.Concat method to use the correct overload.
Proving this works:
Console.WriteLine(Expression.Lambda<Func<string>>(addExpr).Compile()());
Will output:
hello world
Yeah, it's a surprise isn't it!!! The compiler replaces it with a call to String.Concat.

Categories

Resources