8

I am a beginner at C#. I am facing a problem while converting a string to a mathematical expression. I have a UI where user can create formula using random formula field. And in another UI user will give input of those formula field.

like for example at first time the formula may be (a+b)^n and another the formula may be ((a+b+c)^n+b).

In my calculation UI for the first time user will give input for a,b,n and for 2nd formula user will give input for a,b,c,n. Can anyone please help me about how to get the result for both of the formula programmatic-ally? Thanks in advance

ekad
  • 14,436
  • 26
  • 44
  • 46
rafat
  • 819
  • 2
  • 10
  • 25

6 Answers6

6

This is how it should be done:

public class StringToFormula
{
    private string[] _operators = { "-", "+", "/", "*","^"};
    private  Func<double, double, double>[] _operations = {
        (a1, a2) => a1 - a2,
        (a1, a2) => a1 + a2,
        (a1, a2) => a1 / a2,
        (a1, a2) => a1 * a2,
        (a1, a2) => Math.Pow(a1, a2)
    };

    public double Eval(string expression)
    {
        List<string> tokens = getTokens(expression);
        Stack<double> operandStack = new Stack<double>();
        Stack<string> operatorStack = new Stack<string>();
        int tokenIndex = 0;

        while (tokenIndex < tokens.Count) {
            string token = tokens[tokenIndex];
            if (token == "(") {
                string subExpr = getSubExpression(tokens, ref tokenIndex);
                operandStack.Push(Eval(subExpr));
                continue;
            }
            if (token == ")") {
                throw new ArgumentException("Mis-matched parentheses in expression");
            }
            //If this is an operator  
            if (Array.IndexOf(_operators, token) >= 0) {
                while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek())) {
                    string op = operatorStack.Pop();
                    double arg2 = operandStack.Pop();
                    double arg1 = operandStack.Pop();
                    operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
                }
                operatorStack.Push(token);
            } else {
                operandStack.Push(double.Parse(token));
            }
            tokenIndex += 1;
        }

        while (operatorStack.Count > 0) {
            string op = operatorStack.Pop();
            double arg2 = operandStack.Pop();
            double arg1 = operandStack.Pop();
            operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
        }
        return operandStack.Pop();
    }

    private string getSubExpression(List<string> tokens, ref int index)
    {
        StringBuilder subExpr = new StringBuilder();
        int parenlevels = 1;
        index += 1;
        while (index < tokens.Count && parenlevels > 0) {
            string token = tokens[index];
            if (tokens[index] == "(") {
                parenlevels += 1;
            }

            if (tokens[index] == ")") {
                parenlevels -= 1;
            }

            if (parenlevels > 0) {
                subExpr.Append(token);
            }

            index += 1;
        }

        if ((parenlevels > 0)) {
            throw new ArgumentException("Mis-matched parentheses in expression");
        }
        return subExpr.ToString();
    }

    private List<string> getTokens(string expression)
    {
        string operators = "()^*/+-";
        List<string> tokens = new List<string>();
        StringBuilder sb = new StringBuilder();

        foreach (char c in expression.Replace(" ", string.Empty)) {
            if (operators.IndexOf(c) >= 0) {
                if ((sb.Length > 0)) {
                    tokens.Add(sb.ToString());
                    sb.Length = 0;
                }
                tokens.Add(c);
            } else {
                sb.Append(c);
            }
        }

        if ((sb.Length > 0)) {
            tokens.Add(sb.ToString());
        }
        return tokens;
    }
}

Call the class and method like this:

string formula = "type your formula here"; //or get it from DB
StringToFormula stf = new StringToFormula();
double result = stf.Eval(formula);
  • Good effort! But it is not working as desired. For Eg. I tried (12 / 12 * (10 + 10)) / 2 and it is giving 0.025 instead of 10 – Venugopal M Dec 26 '21 at 17:05
  • @VenugopalM I think the error comes from Line 31 where the while loop is looking for IndexOf the 'token' to be less than the IndexOf operatorStack.Peek(). This will ALWAYS evaluate something like '1/2*3' as 2 operations. The use of .Pop() (actually the use of Stacks in general) means that we're always evaluating from right-to-left. If you remove this second condition of the while loop, then the expression is evaluated correctly. The other option would be to change everything from Stack to List and using .RemoveAt(0) so it's evaluated left-to-right. We want FIFO not LIFO. – Michael Sep 13 '22 at 21:12
1

There are plenty methods for formula evaluation, take a look. Just take your input, replace a, b, n chars in your string to values provided by user and resolve equation with one of methods mentioned.

Community
  • 1
  • 1
Tarec
  • 3,268
  • 4
  • 30
  • 47
  • Try flee for example, simple sample provided: http://flee.codeplex.com/wikipage?title=GettingStarted&referringTitle=Examples – Tarec Feb 13 '14 at 10:22
1

i think this is the solution

 Expression e = new Expression("((a+b+c)^n+b)");
 e.Evaluate();
0
string input= "(12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) ( 5 + 12 ))";       
    string str4 = "(" + input`enter code here`.Replace(" ", "") + ")";
            str4 = str4.Replace(")(", ")*(");
            while (str4.Contains('('))
            {
                string sub1 = str4.Substring(str4.LastIndexOf("(") + 1);
                string sub = sub1.Substring(0, sub1.IndexOf(")"));
                string sub2 = sub;
                string str21 = sub2.Replace("^", "~^~").Replace("/", "~/~").Replace("*", "~*~").Replace("+", "~+~").Replace("-", "~-~");
                List<string> str31 = str21.Split('~').ToList();
                while (str31.Count > 1)
                {
                    while (str31.Contains("*"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "*")
                            {
                                val = Convert.ToDouble(str31[i - 1]) * Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("/"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "/")
                            {
                                val = Convert.ToDouble(str31[i - 1]) / Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("+"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "+")
                            {
                                val = Convert.ToDouble(str31[i - 1]) + Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                    while (str31.Contains("-"))
                    {
                        for (int i = 0; i < str31.Count; i++)
                        {
                            if (str31[i] == "-")
                            {
                                val = Convert.ToDouble(str31[i - 1]) - Convert.ToDouble(str31[i + 1]);
                                str31.RemoveRange(i - 1, 3);
                                str31.Insert(i - 1, val.ToString());
                            }
                        }
                    }
                }
                str4 = str4.Replace("(" + sub + ")", str31[0].ToString());
            }

            string sum = str4;
Amit Tiwari
  • 368
  • 1
  • 4
  • this is kind of simple parser. you can enhance it more. below are few related links... http://www.dreamincode.net/forums/topic/35320-reverse-polish-notation-in-c%23/ http://mathosparser.codeplex.com/ http://www.codeproject.com/Articles/274093/Math-Parser-NET http://www.codeproject.com/Articles/88435/Simple-Guide-to-Mathematical-Expression-Parsing http://www.mathparsers.com/math-parser-for-csharp-dotnet/ – Amit Tiwari Feb 13 '14 at 13:47
  • it will work for expression containing add, subtract, multiplication and division and for more you can enhance it... – Amit Tiwari Feb 14 '14 at 10:20
0

The most structural approach which comes to my mind would be to define a grammar consisting out of operator symbols (which are apparently +,-,*,/ and ^ in your case) and operands; then, if a derivation of the input in the defined grammar exists, the derivation basically is the expression tree which can then be traversed recursively while the operators are directry translated to the actual operations. I admit that the description is a bit vague, but good parsing can be a bit difficult. Perhaps a look at LL parser can help a bit.

Codor
  • 17,447
  • 9
  • 29
  • 56
0

convert String To Mathematical Expression

var s3 = "3 - 4 + 5 * 9"

var s4 = NSExpression(format: s3).expressionValue(with: nil, context: nil) as! Double // 44.0

Answer : 44

Patrick
  • 1,189
  • 5
  • 11
  • 19