How can I compute math (3+2)*4 from string to number?

Using VS2016 Community, newest monogame, and C#. The only solution to computing a string I could find is:

string myMathString="(3+2)*4";
double myAnswer=new DataTable().Compute(myMathString, null);

This does not work because DataTable requires using system.data, which cannot be found in monogame projects. Any help would be appreciated I feel that I have hit a brick wall!

You likely just need to add a reference to System.Data. This has nothing to do with restrictions in MonoGame. We don’t add a reference to it by default because it is rarely used in games.

Thank you. I am unable to add References. When I attempt to it gives me this message:

Reference Manager > Framework >
“All of the Framework assemblies are already referenced. Please use the Object Browser to explore the references in the Framework.”

Are you building a Windows Store app or Universal Windows Project? If so, System.Data is not available to those project types.

Yes, do you know of any alternative? Maybe a function someone has made for basic math?

Try http://ncalc.codeplex.com/

Thank you for your help but NCalc seems to require Antlr3.Runtime.dll which is not compatible with monogame.

There’s always the option of writing your own. It shouldn’t be too bad for basic maths. You can split the string first on brackets into “expressions”, then split on the math operators like +, -, / etc… Plus you get the benefit of benifit of being able to modify it for your purposes.

Same idea same time. I really want something that can do complicated math but I got something going just to get my code running. Here it is in case anyone needs to do basic +, -, /, *:

https://codedump.io/share/IHPFKE0PrbqU/1/basic-string-calculator

All primitive valuetypes in c# have a tryparse including double float int ect.

        string stringtotest = "3.14159";
        double valuefromstring = 0d;

        double tempresult = -1d;
        if (double.TryParse(stringtotest, out tempresult))
        {
            valuefromstring = tempresult;
        }
        else
        {
            // the string did not represent a double maybe it was letters
            // so uh you should prolly do something to tell the user or yourself that
        }

This is my preffered way, you can test if a string or part of one, is a number. both getting it if it is and doing something if it is or is not afterwards with the if else or just continuing on. You can further wrap this up into a method to reuse more easily.

You can check if it is a special character such as a addition command by testing the string directly against another string or a character.

To get different segments out of a string. requires breaking it apart with string.Split
Or using Stringbuilder in that case maybe simpler with indexing.
But you can produce a calculator yourself directly with some effort as shawn said.

I also found a nice looking implementation here that looks like it’s fairly expandable, yet lightweight.

OK. You mean not compatible with your project.

What do you mean by “not compatible”?

Just for the hell of it i made a calculator from a string lol.
I was thinking this would be a interesting task. This code is far from pretty its basically just scribbled out as fast as i could. Its probably the ugliest thing i have written in a while anyways.

It wont handle any kind of fractional powers or negative exponents but it handles the basics. Im sure it has some bugs as well. but in a console it looks cool.

if you run this test below in a console window you will get the following output.

________
Current: ((1*(3+4)*1+4^4+1*4+(2/2))-1*10+3.1415/1.579+8)
________
Parsing: ((1*     *1+4^4+1*4+(2/2))-1*10+3.1415/1.579+8)
Segment:  3+4
  segment=7
________
Current: ((1*7*1+4^4+1*4+(2/2))-1*10+3.1415/1.579+8)
________
Parsing: ((1*7*1+4^4+1*4+     )-1*10+3.1415/1.579+8)
Segment:  2/2
  segment=1
________
Current: ((1*7*1+4^4+1*4+1)-1*10+3.1415/1.579+8)
________
Parsing: (                 -1*10+3.1415/1.579+8)
Segment:  1*7*1+4^4+1*4+1
  segment=1*7*1+16+1*4+1
  segment=7*1+16+1*4+1
  segment=7+16+1*4+1
  segment=7+16+4+1
  segment=23+4+1
  segment=27+1
  segment=28
________
Current: (28-1*10+3.1415/1.579+8)
________
Parsing:
Segment:  28-1*10+3.1415/1.579+8
  segment=28-10+3.1415/1.579+8
  segment=28-10+1.98955+8
  segment=18+1.98955+8
  segment=19.98955+8
  segment=27.98955
________
Current: 27.98955
________
Final: 27.98955
answer is 27.98955

The code follows

    public void Test()
    {
        string mathstring = "(1*(3+4)*1+4^4+1*4+(2/2))-1*10+3.1415/1.579+8";
        float n = MathOperation.ParseLine(mathstring);
        Console.WriteLine("answer is " + n.ToString());
    }
    public class MathOperation
    {
        static char blank = ' ';
        static StringBuilder originaltext = new StringBuilder();
        // index to location
        static List<int> leftpara = new List<int>();
        static List<int> rightpara = new List<int>();
        static string[] op_string = new string[]{"Power of","Multiply by","Divide by","Add by","Subtract by"};
        static char[] op_char = new char[] { '^', '*', '/', '+', '-' };
        public static float ParseLine(string s)
        {
            float result = 0f;
            originaltext.Append(s);
            originaltext.Append(')');
            originaltext.Insert(0,'(');
            Console.WriteLine("________");
            Console.WriteLine("Current: "+ originaltext.ToString());
            FindParenthisisLists(originaltext);
            ParseInnerMostParenthesis();
            //
            result = CalculateLine(originaltext);
            originaltext.Length = 0;
            originaltext.Insert(0, result);
            originaltext = TrimAll(originaltext);
            Console.WriteLine("________");
            Console.WriteLine("Final: " + originaltext.ToString());
            return result;
        }
        private static void FindParenthisisLists(StringBuilder s)
        {
            leftpara.Clear();
            rightpara.Clear();
            int i = 0;
            while (i < s.Length)
            {
                if (s[i] == '(') {leftpara.Add(i);}
                if (s[i] == ')') { rightpara.Add(i); }
                i++;
            }
        }
       
        private static int ParseInnerMostParenthesis()
        {
            int result = -1;
            while (leftpara.Count > 0)
            {
                for (int j = 0; j < rightpara.Count; j++)
                {
                    int temprightmostvalidpos = rightpara[j];
                    int templeftmostvalidpos = -1;
                    int lefttoremove = -1;
                    for (int i = 0; i < leftpara.Count; i++)
                    {
                        if (leftpara[i] < rightpara[j] && leftpara[i] > templeftmostvalidpos)
                        {
                            templeftmostvalidpos = leftpara[i];
                            lefttoremove = i;
                        }
                    }
                    // checksafetoremove
                    if (lefttoremove >= 0)
                    {
                        originaltext[templeftmostvalidpos] = blank;
                        originaltext[temprightmostvalidpos] = blank;
                        StringBuilder seg = new StringBuilder(16);
                        int k = 0;
                        for (int pos = templeftmostvalidpos; pos < temprightmostvalidpos; pos++)
                        {
                            seg.Append(originaltext[pos]);
                            originaltext[pos] = blank;
                            k++;
                        }
                        Console.WriteLine("________");
                        Console.WriteLine("Parsing: " + originaltext.ToString());
                        Console.WriteLine("Segment: " +seg.ToString());
                        float value = CalculateLine(seg);
                        originaltext.Insert(templeftmostvalidpos, value);
                        originaltext = TrimAll(originaltext);
                        Console.WriteLine("________");
                        Console.WriteLine("Current: " +originaltext.ToString());
                        j = rightpara.Count;
                    }
                }
                FindParenthisisLists(originaltext);
            }
            return result;
        }
        private static float CalculateLine(StringBuilder line)
        {
            float result = 0;
            // check we have a operation left
            int firstindex = 0;
            while (firstindex > -1)
            {
                firstindex = -1;
                // check for left most index in line and mark if found
                int optype = -1;
                // check lowest priority first
                for (int i = line.Length-1; i >= 0; i--)
                {
                    if (line[i] == '+') { optype = 3; firstindex = i; }
                    if (line[i] == '-') { optype = 4; firstindex = i; }
                }
                // this has secondary priority
                for (int i = line.Length-1; i >= 0; i--)
                {
                    if (line[i] == '*') { optype = 1; firstindex = i; }
                    if (line[i] == '/') { optype = 2; firstindex = i; }
                }
                // this one has priority
                for (int i = line.Length-1; i >= 0; i--)
                {
                    if (line[i] == '^') { optype = 0; firstindex = i; }
                }
                // a bi-directional sort calculate and replace
                // im just going to do it all at once.
                if (firstindex > -1)
                {
                    // clear out the actual operator char that was found
                    line[firstindex] = blank;
                    // prep our loop
                    int left = firstindex;
                    int right = firstindex;
                    bool leftstoped = false;
                    bool rightstoped = false;
                    float leftval = 0;
                    float rightval = 0;
                    StringBuilder sbleft = new StringBuilder();
                    StringBuilder sbright = new StringBuilder();
                    int i = 0;
                    while (i < line.Length)
                    {
                        if (leftstoped == false) 
                        { 
                            left -= 1; 
                        }
                        if (rightstoped == false) 
                        { 
                            right += 1; 
                        }
                        //_____
                        if (left < 0) { leftstoped = true; left = 0; }
                        if (leftstoped == false )
                        {
                            char c = line[left];
                            if (c == '^' || c == '/' || c == '*' || c == '+' || c == '-')
                            {
                                leftstoped = true;
                                left += 1;;
                            }
                            else
                            {
                                sbleft.Insert(0,c);
                                line[left] = blank;
                            }
                        }
                        if (right >= line.Length) { rightstoped = true; right = line.Length - 1; }
                        if (rightstoped == false )
                        {
                            char c = line[right];
                            if (c == '^' || c == '/' || c == '*' || c == '+' || c == '-')
                            {
                                rightstoped = true;
                                right -= 1;
                            }
                            else
                            {
                                sbright.Insert(0,c);
                                line[right] = blank;
                            }
                        }
                        i++;
                    }
                    // we have exited our while loop, so we reverse the right string
                    sbright = Reverse(sbright);
                    // we parse solve and readd
                    if(parseFloat(sbleft.ToString(), ref leftval) && parseFloat(sbright.ToString(), ref rightval) )
                    {
                        float repval = 0f;
                        if (optype == 0) { for (int z = 1; z < rightval; z++) { repval = leftval * leftval; } }
                        if (optype == 1) { repval = leftval * rightval; }
                        if (optype == 2) { repval = leftval / rightval; }
                        if (optype == 3) { repval = leftval + rightval; }
                        if (optype == 4) { repval = leftval - rightval; }
                        string repstr = repval.ToString();
                        line.Insert(left, repstr);
                    }
                    else
                    {
                        Console.WriteLine("  !!!parse failed on: " + sbleft.ToString() + " or " + sbright.ToString() );
                    }
                    line = TrimAll(line);
                    Console.WriteLine("  segment=" + line.ToString());         
                }
            }
            parseFloat(line.ToString(), ref result);
            return result;
        }
        /// <summary>
        /// returns true on successful parse
        /// if the parse fails the value remains unchanged
        /// </summary>
        public static bool parseFloat(string s, ref float f)
        {
            s = s.Trim();
            bool result = false;
            double tempresult = 0d;
            if (double.TryParse(s, out tempresult))
            {
                f = (float)tempresult;
                result = true;
            }
            else
            {
                // the string did not represent a double maybe it was letters
                // so uh you should prolly do something to tell the user or yourself that
                // Log.Tracing.Warning("Event: parsing from string to number failed");
            }
            return result;
        }
        public static StringBuilder TrimAll(StringBuilder s)
        {
            StringBuilder str = new StringBuilder();
            for (int i = 0; i < s.Length;i++ )
            {
                if (s[i] != ' ') { str.Append(s[i]); }
            }
            s = str;
            return s;
        }
        public static StringBuilder Reverse(StringBuilder s)
        {
            StringBuilder str = new StringBuilder();
            str.Append(s);
            for (int i = 0; i < str.Length;i++)
            {
                int j = (str.Length -1) - i;
                s[j] = str[i];
            }
            return s;
        }
    }

ya i was getting tired when i was writing this i see a bunch of bugs in it lol
like the ^ function is wrong.
I didn’t check for the farthest left special case operator negative sign.
I dunno why i even converted it back to float after parsing.
I didn’t break half the loops when they were done early.
It makes a ton and a half of garbage lol.
Its pretty sloppy, but you could fix it up i suppose.

Adding to the above, in particular to what @willmotil posted.

Look into System.String MSDN and System.Text.StringBuilder MSDN, particularly the String Formatting aspects such as the Split commands… [EDIT and Replace/Append etc.]

You could utilise the methods of parsing your string into the key components by identifying each Char and converting them to Numerical and Expression values.

I am not a pro but I hope this helps.

I converted this Java project to C# for use in XNA / MonoGame.
https://java.net/projects/eval/pages/Home

If it look interesting to you I can upload the code.

Don’t know if you’re still looking for a solution, but I had a lot of fun writing a “true” parser for this purpose.

(I’ve since lost the code though). If I recall, it used a Pratt Parser and a basic tokenizer. While I went a bit over-the-top and supported things like logical branches and variables/functions within evaluated strings, it was still quite simple.
The best parts: minimum string hackery and it’s easily reused (particularly if exposed as a library) should the need ever arise again.

This by the way, was the first thing of the sort I had ever written.
Here are the resources I learned from:
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
http://effbot.org/zone/simple-top-down-parsing.htm

And then there’s the obligatory link to Douglas Crockford’s javascript stuff:
http://javascript.crockford.com/tdop/tdop.html

And the original paper:
https://tdop.github.io/

EDIT: Don’t be dismayed if the links look confusing. I read and re-read them (the first three, tbh I’ve never read the real research paper) several times trying to understand it all until I decided “This is ridiculous. Time to jump right in and try to implement it.”. I was amazed at the simplicity once I finally started coding.

IF you were up to it… You could just write an RPN evaluator. Basically convert the equation from Infix to Postfix and evaluate. You obviously would need to tokenize the string first.

I wrote one a couple years ago and its actually very easy. Plus its actually kinda fun to implement and see it working on various equations :smiley: