Ever need to give your users the ability to enter in simple formulas into a TextBox and have it evaluated? This is something I keep running across and after looking around a bit I couldn’t find a simple light-weight formula evaluator for C#, so I decided to build one.

What I wanted as a simple to use evaluator that accepts a string that looks similiar to this: “5 * 2 / 3″, and returns the correct answer.

The formula evaluator I created supports multiplication, division, addition, subtraction, powers, sin, cos and obeys brackets following the correct order of operations.

**Formula Evaluator**

using System; using System.Text.RegularExpressions; namespace JarlooFormulaEvaluator { public class FormulaEvaluator { private readonly Regex bracketsRegex = new Regex(@"([a-z]*)\(([^\(\)]+)\)(\^|!?)", RegexOptions.Compiled); private readonly Regex cosRegex = new Regex(@"cos(-?\d+.?\d*)", RegexOptions.Compiled); private readonly Regex sinRegex = new Regex(@"sin(-?\d+.?\d*)", RegexOptions.Compiled); private readonly Regex powerRegex = new Regex(@"(-?\d+\.?\d*)\^(-?\d+\.?\d*)", RegexOptions.Compiled); private readonly Regex multiplyRegex = new Regex(@"(-?\d+\.?\d*)\*(-?\d+\.?\d*)", RegexOptions.Compiled); private readonly Regex divideRegex = new Regex(@"(-?\d+\.?\d*)/(-?\d+\.?\d*)", RegexOptions.Compiled); private readonly Regex addRegex = new Regex(@"(-?\d+\.?\d*)\+(-?\d+\.?\d*)", RegexOptions.Compiled); private readonly Regex subtractRegex = new Regex(@"(-?\d+\.?\d*)-(-?\d+\.?\d*)", RegexOptions.Compiled); public double Evaluate(string expr) { expr = expr.Replace(" ", "").ToLower(); Match m = bracketsRegex.Match(expr); while (m.Success) { expr = expr.Replace("(" + m.Groups[2].Value + ")", Solve(m.Groups[2].Value)); m = bracketsRegex.Match(expr); } return Convert.ToDouble(Solve(expr)); } private string Solve(string expr) { if (expr.IndexOf("cos") != -1) expr = Do(cosRegex, expr, (x) => Math.Cos(Convert.ToDouble(x.Groups[1].Value)).ToString()); if (expr.IndexOf("sin") != -1) expr = Do(sinRegex, expr, (x) => Math.Sin(Convert.ToDouble(x.Groups[1].Value)).ToString()); if (expr.IndexOf("^") != -1) expr = Do(powerRegex, expr, (x) => Math.Pow(Convert.ToDouble(x.Groups[1].Value), Convert.ToDouble(x.Groups[2].Value)).ToString()); if (expr.IndexOf("/") != -1) expr = Do(divideRegex, expr, (x) => (Convert.ToDouble(x.Groups[1].Value) / Convert.ToDouble(x.Groups[2].Value)).ToString()); if (expr.IndexOf("*") != -1) expr = Do(multiplyRegex, expr, (x) => (Convert.ToDouble(x.Groups[1].Value) * Convert.ToDouble(x.Groups[2].Value)).ToString()); if (expr.IndexOf("+") != -1) expr = Do(addRegex, expr, (x) => (Convert.ToDouble(x.Groups[1].Value) + Convert.ToDouble(x.Groups[2].Value)).ToString()); if (expr.IndexOf("-") != -1) expr = Do(subtractRegex, expr, (x) => (Convert.ToDouble(x.Groups[1].Value) - Convert.ToDouble(x.Groups[2].Value)).ToString()); return expr; } private static string Do(Regex regex, string formula, Func<Match, string> func) { MatchCollection collection = regex.Matches(formula); if (collection.Count == 0) return formula; for (int i = 0; i < collection.Count;i++ ) formula = formula.Replace(collection[i].Groups[0].Value, func(collection[i])); formula = Do(regex, formula, func); return formula; } } }

And to use the code you need could do something like this:

namespace JarlooFormulaEvaluator { internal class Program { private static void Main(string[] args) { FormulaEvaluator eval = new FormulaEvaluator(); //Simple math Console.WriteLine(string.Format("5*2={0}", eval.Evaluate("5*2"))); //Default order of operations Console.WriteLine(string.Format("5*2+3={0}", eval.Evaluate("5*2+3"))); //Brackets are solved first Console.WriteLine(string.Format("5*(2+3)={0}", eval.Evaluate("5*(2+3)"))); //Can use sin / cos Console.WriteLine(string.Format("5*(2+sin(3))={0}", eval.Evaluate("5*(2+sin(3))"))); //Can raise to the power of Console.WriteLine(string.Format("5*(2+sin(3))/2^2={0}", eval.Evaluate("5*(2+sin(3))/2^2"))); Console.ReadLine(); } }

You can easily add new functions by including a new Regex and making a new entry in the Solve method.

### 24 Comments

Join the conversation and post a comment.

I’m new to C# and wanted to understand the logic behind your parser. However i can’t seem to understand the source code. I tried to compile the code to sebug and step through however i ge many errors, particularly in the Solve and Do methods. It seems that there are random => and < etc in the code that dont compile. I also cant make out what the parameters are for the Do method. Would you mind posting again so i can get to grips with this project.

By the way i have found your site very useful and the design is excellent.

Regards

Sorry about that. I changed the module used to display code on the site and obviously missed updating this article. It’s now fixed.

Thanks! I think i understand delegates and lambda expressions a lot more now. Just out of interest how would you implement the evaluation of Pi or other constants in this example? Would it be best to replace them with the actual value before evaluation or create another Regex to deal with them?

I would create another regex just like the COS and SIN ones to handle PI or constants.

Wouldn’t they have to be slightly different though as you don’t have to have two operands when using constants. Would it end up replacing the words with the constant via a Regex rather than using the .Replace method?

Your right. Replacing would be easier.

For example in the Evaluate method you could do this for PI:

expr = expr.Replace(“pi”, Math.PI.ToString()).ToLower();

You could do the same for other constants, or better yet write a method that lets you assign constants to the engine. Could be as simple as a public method like: Constants {get; set;}

public Dictionary

Then iterate it at the beginning of the Evaluate method and do the replacements.

Great suggestion! Ona slightly different note is there a way to slightly modify the Regex’s so that they accept the operand in brackets as well as without? e.g sin(45) rather than just sin45.

Thanks

It already does. Since it does order of operations it will solve for the brackets first then essentially replace the brackets and their contents with the value. Then it will solve from there.

So if you enter sin(45), it will replace (45) with 45, so you have sin45, then it will solve that and it will match the regex and process properly.

You could also do sin(45-10) and it will work properly since it does the brackets first.

I was just thinking of a method to put brackets around the whole expression e.g (sin(45) instead of sin(45) as there is a bug if you do for example sin(45) + cos(45), fixed by putting brackets around each individual expression

Managed to fix the bug by looping through all of the regex’s in a set and inserting brackets encapsulating each expression found in a MatchCollection. Also had to do this twice – once at the beginning of the Evaluate method and once after the brackets had been matched. Also had to match brackets again after brackets had been put in.

Not the best fix in terms of efficiency, but it works well.

Write unit test for FormulaEvaluator. Wrong calculation for decimals value. Example, 5,1-2,9

To fix bug change “.?” to “.?” in all regexp!

What do you mean by wrong calculation for decimals value? All tests that iv done turn out perfecty. What do you mean by 5,1-2,9?

Oh sorry, i see what you mean now. Its because this evaluator does not support comma’s as a method of signifying decimal points, only periods. As you said if you want to change it to accept comma’s, you can modify the regex expressions

Is this a localization issue? Are the “,” in your example used to denote the difference between dollars and cents? ie: a decimal place like “.” for U.S.

Hi again,

Sorry to bother you again but i was wondering if you might give me some pointers. Its kind of related to this topic in terms of Regex’s.

I am in the middle of a project involving some functions on equations such as gradient(x^2 + 7x +12). I was thinking that i could modify some of the Regex expressions above to return the value 7 in this case. However i came to a halt in the large matter of the brackets. At the moment, like above, the brackets are done first (which is fine) however when it comes to doing the calculation on the equation, the program doesnt know where the equation ends and would simply go on and on to the rest of the expression – resulting in the complete wrong result.

I was wondering if you knew a possible solution to this, where brackets are still taken into consideration, yet the gradient function for example can still work properly.

Thanks again

Formula below won’t work for example…I believe somehow the exponent E is messing it up.

(-4.1855)^2+(0.0000005)^3+(-2.1521)^2

Your regular expression definitions are missing the back slashes before the ‘d’ and special characters. Fixed that but still having problems.

As you mentioned the regex were missing some of the backslashes. (format issue when I posted it. sorry about that.)

I’ve corrected the issue. Should work for you now.

Merci cent millions de fois! Très puissant!

my english is not very good: (

receive health care. very nice sharing.

I’m new in c #. I got an error like this program.

Convert.ToDouble return (Solve (expr)); / Input string was not in a correct format.

If you help rejoice. Thank you.

HASAN , check the formula you are passing in. Could it be incorrect?

Try something simple like: eval.Evaluate(“5*2″)

I got the same error that you got when I had an incorrect forumla. I had more “(“‘s than “)”‘s so it couldn’t evaluate properly- when I fixed my formula it worked.

Good luck.

that’s nice, thank u so much :)

Good luck

Hi,

i am using your formula evaluator.it is not working in some cases .could you please help me.

for example

0+0+0.00+0.00+0.00

this format not working

I´ve found a problem

something like 1+1+5+1+1000

on the replace returned 2+5+2000

then, i´ve changed the code:

for (int i = 0; i < collection.Count;i++ )

formula = formula.Replace(collection[i].Groups[0].Value, func(collection[i]));

for the code:

for (int i = 0; i < collection.Count; i++)

{

//formula = formula.Replace(collection[i].Groups[0].Value, func(collection[i]));

string first_operation = collection[i].Groups[0].Value;

int len = first_operation.Length;

int pos = formula.IndexOf(first_operation);

string formula_before_operation = formula.Substring(0, pos);

string formula_after_operation = formula.Substring(pos+len, formula.Length – (pos+len));

formula = formula_before_operation + func(collection[i]) + formula_after_operation;

}