Greg
Greg

Reputation: 8784

String Fraction to Double

I need a function to parse a user inputs of numbers to doubles. I cannot do anything client side or change how the input comes in.

Input       | Desired Output
"9"         | 9
"9 3/4"     | 9.75
" 9  1/ 2 " | 9.5
"9 .25"     | 9.25
"9,000 1/3" | 9000.33
"1/4"       | .25

I saw this post, but it uses Python, I was just wondering if anybody knew any fancy C# ways of handling this before I spend time to write my own.

Upvotes: 9

Views: 5909

Answers (8)

YevT
YevT

Reputation: 1

Solution below won't work for negative fractions. Can be improved by changing

    //is it a valid fraction?
    if (denominator != 0)
    {
        d = whole + (numerator / denominator);
    }
    to
    //is it a valid fraction?
    if (denominator != .0)
    {
        var sign = Math.Sign(whole);

        d = whole + sign*(numerator/denominator);
    }

Upvotes: 0

Greg
Greg

Reputation: 8784

Here's what I ended up using:

private double ParseDoubleFromString(string num)
{
    //removes multiple spces between characters, cammas, and leading/trailing whitespace
    num = Regex.Replace(num.Replace(",", ""), @"\s+", " ").Trim();
    double d = 0;
    int whole = 0;
    double numerator;
    double denominator;

    //is there a fraction?
    if (num.Contains("/"))
    {
        //is there a space?
        if (num.Contains(" "))
        {
            //seperate the integer and fraction
            int firstspace = num.IndexOf(" ");
            string fraction = num.Substring(firstspace, num.Length - firstspace);
            //set the integer
            whole = int.Parse(num.Substring(0, firstspace));
            //set the numerator and denominator
            numerator = double.Parse(fraction.Split("/".ToCharArray())[0]);
            denominator = double.Parse(fraction.Split("/".ToCharArray())[1]);
        }
        else
        {
            //set the numerator and denominator
            numerator = double.Parse(num.Split("/".ToCharArray())[0]);
            denominator = double.Parse(num.Split("/".ToCharArray())[1]);
        }

        //is it a valid fraction?
        if (denominator != 0)
        {
            d = whole + (numerator / denominator);
        }
    }
    else
    {
        //parse the whole thing
        d = double.Parse(num.Replace(" ", ""));
    }

    return d;
}

Upvotes: 2

shenhengbin
shenhengbin

Reputation: 4294

Is it useful to you ?

I think you can also use the dynamically compiling code

    static void Main(string[] args)
    {
        var value = "9 3/4";
        value = value.Split(' ')[0] + "d + " + value.Split(' ')[1] + "d";

        var exp = " public class DynamicComputer { public static double Eval() { return " + value + "; }}";

        CodeDomProvider cp = new Microsoft.CSharp.CSharpCodeProvider();
        ICodeCompiler icc = cp.CreateCompiler();
        CompilerParameters cps = new CompilerParameters();
        CompilerResults cres;

        cps.GenerateInMemory = true;

        cres = icc.CompileAssemblyFromSource(cps, exp);

        Assembly asm = cres.CompiledAssembly;

        Type t = asm.GetType("DynamicComputer");

        double d = (double)t.InvokeMember("Eval",
            BindingFlags.InvokeMethod,
            null,
            null,
            null);

        Console.WriteLine(d);

        Console.Read();
    }

Upvotes: 0

Joel Coehoorn
Joel Coehoorn

Reputation: 416059

I see two sections. Everything before the first space is the integral section. Everything after the first space is the fractional section. After you separate the two sections, you can just strip spaces from the fractional section, split that section on the / character, and divide the first part by the 2nd part (if there is a 2nd part). Then add the result to the integral section to find your answer.

This algorithm should give a correct result for each of your samples. It might also give an incorrect result for samples like these: "9 .25/4" or "9 3/0", so those are things to watch for. Other things include leading whitespace, whether you want to allow other whitespace, currency symbols, whether "9.25" (no spaces) is a valid input, and what to do with irrational fractions like "1/3", "1/10" (irrational in binary), etc.

I'm not normally a huge believer in test driven design (that you should write the tests first and go for 100% coverage) for static-typed languages, but I do think unit tests have value in certain specific situations, and this is one of those situations. I would put together a few tests for both some common and edge cases, such that you can be sure whatever you end up with handles the inputs correctly to pass the tests.

Upvotes: 3

Ahmadali Shafiee
Ahmadali Shafiee

Reputation: 4657

I wrote this method for this work:

private double DoWork(string data)
    {
        double final = 0;

        foreach (string s in data.Split(' '))
        {
            if (s.Contains('/'))
            {
                final += double.Parse(s.Split('/')[0]) / double.Parse(s.Split('/')[1]);
            }
            else
            {
                double tryparse = 0;
                double.TryParse(s, out tryparse);
                final += tryparse;
            }
        }

        return final;
    }

Upvotes: 0

Ry-
Ry-

Reputation: 225095

I would use regular expressions for this one:

Regex re = new Regex(@"^\s*(\d+)(\s*\.(\d*)|\s+(\d+)\s*/\s*(\d+))?\s*$");
string str = " 9  1/ 2 ";
Match m = re.Match(str);
double val = m.Groups[1].Success ? double.Parse(m.Groups[1].Value) : 0.0;

if(m.Groups[3].Success) {
    val += double.Parse("0." + m.Groups[3].Value);
} else {
    val += double.Parse(m.Groups[4].Value) / double.Parse(m.Groups[5].Value);
}

Untested, as of yet, but I think it should work.

Here's a demo, and here's another demo.

Upvotes: 5

Oded
Oded

Reputation: 499182

There is nothing built in the BCL that will do this, but there are plenty of existing mathematical expression parsers that will (though this may be over the top for this specific situation).

Writing one yourself, for the limited use cases you have posted shouldn't be difficult.

Upvotes: 3

escargot agile
escargot agile

Reputation: 22389

It doesn't look very difficult to write some code that would do this. First try removing all spaces and see if it's a legal number. If it's not, find the legal numbers (such as 9, 3, 4 in "9 3/4" and do a simple arithmetic operation: 9 + 3 / 4 = 9.75

Upvotes: 1

Related Questions