Simple and practical algorithm — calculating mathematical expression

Time:2021-12-30

Algorithm overview

Variable definition:STR mathematical expression
Note: numerical value of mathematical expressionSupport decimal, symbolOnly + – * / ()These.

Calculation principle:: first convert the string of mathematical expression (infix expression) toPostfix Expression , and then evaluate the value of the suffix expression.
Note: in order to improve the accuracy of calculation results, it is used uniformly in the calculation processdecimalType of data.

Example: enter the expression “10 * 1.1 / (2 + 8) + 1.1 + 2.2-4.3” and output the result “0.1”.

Algorithm code (c#)

The code is as follows:

//Test algorithm
class Program
{
    static void Main(string[] args)
    {
        string str = "10*1.1/(2+8)+1.1+2.2-4.3";
        decimal res = Calculator.Calculate(str);
        Console.WriteLine(str+"="+res);
        Console.ReadLine();
    }        
}

/// 
///Calculate the mathematical expression. Based on the implementation of suffix expression, you can use + - * / () operator
/// 
class Calculator 
{
    /// 
    ///Calculates the value of a mathematical expression
    /// 
    ///Mathematical expression
    /// 
    public static decimal Calculate(string str)
    {            
        try
        {
            Queue queue = CreateRPN(str);
            decimal res = ParseRPN(queue);
            return res;
        }
        catch (OverflowException)
        {
            Throw new exception ("calculation overflow caused by too large data");
        }
        catch (Exception)
        {
            Throw new exception ("unable to evaluate the wrong expression");
        }
            
    }

    //Generate suffix expression
    private static Queue CreateRPN(string str)
    {
        //Stack of temporary storage + - * / (symbols)
        Stack stack = new Stack();
        //A queue that stores suffix expressions
        Queue queue = new Queue();
        for (int i = 0; i < str.Length; )
        {
            //If it is a space, skip directly
            if (str[i] == ' ')
            {
                i++;
                continue;
            }
            else if ((str[i] >= '0' && str[i] <= '9') || (str[i] == '.'))
            {
                //Current number
                decimal cur = 0;
                //Decimal identification
                bool isDecimal = false;
                //Decimal places
                int num = 0;
                //Pay special attention to the condition I < s.length
                while (i < str.Length && ((str[i] >= '0' && str[i] <= '9') || (str[i] == '.')))
                {
                    if (str[i] == '.')
                    {
                        isDecimal = true;
                    }
                    else
                    {
                        if (!isDecimal)
                        {
                            cur = cur * 10 + str[i] - '0';
                        }
                        else
                        {
                            num++;
                            cur = cur + ((decimal)(str[i] - '0')) / (decimal)(Math.Pow(10, num));
                        }
                    }
                    i++;
                }
                queue.Enqueue(cur.ToString());
            }
            else if (str[i] == ')')
            {
                //If it is ")", you need to pop up the operation symbol in the stack and add it to the queue of suffix expression
                //Until "(" in the symbol stack is encountered
                while (stack.Count != 0 && stack.Peek() != '(')
                {
                    queue.Enqueue(stack.Pop() + "");
                }
                stack.Pop();
                i++;
            }
            else
            {
                //It may be + - * / these symbols or left parentheses
                //At this time, you need to determine the priority of the top element of the symbol stack and the currently traversed character
                while (stack.Count != 0 && Compare(stack.Peek(), str[i]) < 0)
                {
                    queue.Enqueue(stack.Pop() + "");
                }
                stack.Push(str[i]);
                i++;
            }
        }
        while (stack.Count != 0)
        {
            queue.Enqueue(stack.Pop() + "");
        }
        return queue;
    }

    //Processing symbol priority
    private static int Compare(char peek, char c)
    {
        if (peek == '(' || c == '(') return 1;
        if (c == '+' || c == '-') return -1;
        if (c == '*' && (peek == '*' || peek == '/')) return -1;
        if (c == '/' && (peek == '*' || peek == '/')) return -1;
        return 1;
    }

    //Resolve suffix expression
    private static decimal ParseRPN(Queue queue)
    {
        //Result stack
        Stack res = new Stack();
        while (queue.Count != 0)
        {
            String t = queue.Dequeue();
            if (t.Equals("+") || t.Equals("-") || t.Equals("*") || t.Equals("/"))
            {
                decimal a = res.Pop();
                decimal b = res.Pop();
                decimal result = Calculate(b, a, t);
                res.Push(result);
            }
            else
            {
                res.Push(decimal.Parse(t));
            }
        }
        return res.Pop();
    }

    //Basic arithmetic unit
    private static decimal Calculate(decimal a, decimal b, String t)
    {
        //Calculate
        if (t.Equals("+"))
        {
            return a + b;
        }
        else if (t.Equals("-"))
        {
            return a - b;
        }
        else if (t.Equals("*"))
        {
            return a * b;
        }
        else
        {
            return a / b;
        }
    }
}

Note: the above code can be extended to support more complex operators

Algorithm implementation

Rules for converting infix expression to suffix expression: traverse each number and symbol of infix expression from left to right. If it is a number, it will be output, that is, it will become a part of suffix expression; If it is a symbol, it is judged that its priority with the top symbol is the right bracket or the priority is lower than the top symbol (multiplication and division priority plus and minus), then the top elements of the stack are found and output in turn, and the current symbol is put on the stack until the suffix expression is finally output.

Example: infix expression “9 + (3-1)3 + 10 / 2 “is converted to suffix expression” 9 3 1-3 “+ 10 2/+”。

Rules for evaluating suffix expressions: traverse each number and symbol of the expression from left to right. If it is a number, it will enter the stack. If it is a symbol, it will take the two numbers at the top of the stack out of the stack for operation. The operation result will enter the stack until the final result is obtained.

Extension: use datatable Compute calculation

A simpler way,Use datatable Compute computes mathematical expressions, the code is as follows:

//Test algorithm
static void Main(string[] args)
{
    Console.WriteLine(Calculate("10*1.1/(2+8)+1.1+2.2-4.3"));
    Console.WriteLine(Calculate(double.MaxValue+"+"+double.MaxValue));  
    Console.ReadLine();
}

/// 
///Calculates the value of a mathematical expression
/// 
///Mathematical expression
///Calculation results
private static double Calculate(string str)
{
    try
    {
        DataTable dt = new DataTable();
        double result = double.Parse(dt.Compute(str, "").ToString());
        return result;
    }
    catch (OverflowException)
    {
        Throw new exception ("calculation overflow caused by too large data");
    }
    catch (Exception)
    {
        Throw new exception ("unable to evaluate the wrong expression");
    }            
}

*Note:DataTable. The results of compute calculation include decimal and double (tested), I guess that the precision loss of double operation will not occur in the operation within the decimal value range, but the calculation result can only be represented by the double type with a large range.

For now,DataTable. Compute computes mathematical expressions with a more comprehensive scope of application

Extensions: evaluating mathematical expressions using SQL

Can passExecute the SQL statement to get the result of the mathematical expression, the SQL statement is as follows:

string strSQL="SELECT "+"10*1.1/(2+8)+1.1+2.2-4.3";

The benefits of using SQL statements areIt can calculate mathematical expressions containing more advanced operations such as square and square, the most simple and convenient is to useSQLite (no database communication overhead)Database to calculate the mathematical expression. The code is as follows:

/// 
///Evaluates the given expression
/// 
///Expression
/// 
public static object SQLiteCompute(string expr)
{            
    expr = expr.Replace("/", "*1.0/");
    expr = expr.Replace("[", "(");
    expr = expr.Replace("]", ")");
    expr = expr.Replace("{", "(");
    expr = expr.Replace("}", ")");            

    string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DataBass");
    if (!Directory.Exists(path)) Directory.CreateDirectory(path);

    string connStr = Path.Combine(path, "ComputeEngine.db");
    if (!File.Exists(connStr)) File.Create(connStr).Close();

    using (SQLiteConnection conn = new SQLiteConnection("Data Source=" + connStr))
    {
        if (conn.State != ConnectionState.Open)
            conn.Open();
        var cmd = new SQLiteCommand();
        
        cmd.Parameters.Clear();
        cmd.Connection = conn;
        cmd.CommandText = "SELECT " + expr;
        cmd.CommandType = CommandType.Text;
        cmd.CommandTimeout = 30;                
        return cmd.ExecuteScalar();
    }
}

usage method:

static void Main(string[] args)
{
    string result = SQLiteCompute("sqrt(1+2)/[4+(1+1)/3]").ToString();
    Console.WriteLine(result);

    Console.ReadKey();
}

Calculation results:

0.371153744479045

For information about using SQLite database, you can refer toUse of SQLite and tool class in C #

reference material

Mathematical expression of stack implementation computation — CSDN
Contact suffix expression (inverse Polish representation) – Veda
Convert infix expression to suffix expression – Veda
Calculation process of graphic suffix expression — Veda
How to calculate the value of an expression in C # – CSDN