Click here to Skip to main content
15,881,812 members
Articles / Programming Languages / C#
Tip/Trick

Convert Decimal to Fraction and Vice Versa in C#

Rate me:
Please Sign up or sign in to vote.
3.77/5 (4 votes)
20 Jul 2013CPOL1 min read 60.2K   9   20
Class for converting decimals and fractions (allows rounding to a decimal also)

Introduction

Recently, I was challenged with writing a report to generate a quote for an engineering specification. One of the requirements for this report was to convert decimals to fractions, and vice versa. It was also required to round the fractions to the nearest 1/16 of an inch.

Background

I had run several internet searches to help me solve these issues and turned up some interesting stuff, but nothing to "do it all." So I combined what I found out there into one class, made some modifications, used it in my report, then decided to share this with my comrades here on CodeProject. My hope is that this class can be utilized, expanded upon, and / or optimized by the community.

Using the Code

I used Visual Studio to compile and run the class. Writing a test app to use this class should be simple enough.  If not, let me know and I'll push something up here.

Notes

  • I default the fractions to round to 1/16 as this was the requirement for the report. You can skip rounding all together by not passing in the last two parameters into the "Convert" function returning the string (fraction) value.
  • I wrote a handy little formatter for feet and inches that might help someone too (another requirement for the report I had to write).
C#
public static class FractionConverter
{
    public static string Convert(decimal pvalue,
    bool skip_rounding = false, decimal dplaces = (decimal)0.0625)
    {
        decimal value = pvalue;

        if (!skip_rounding)
            value = FractionConverter.DecimalRound(pvalue, dplaces);

        if (value == Math.Round(value, 0)) // whole number check
            return value.ToString();

        // get the whole value of the fraction
        decimal mWhole = Math.Truncate(value);

        // get the fractional value
        decimal mFraction = value - mWhole;

        // initialize a numerator and denomintar
        uint mNumerator = 0;
        uint mDenomenator = 1;

        // ensure that there is actual a fraction
        if (mFraction > 0m)
        {
            // convert the value to a string so that
            // you can count the number of decimal places there are
            string strFraction = mFraction.ToString().Remove(0, 2);

            // store the number of decimal places
            uint intFractLength = (uint)strFraction.Length;

            // set the numerator to have the proper amount of zeros
            mNumerator = (uint)Math.Pow(10, intFractLength);

            // parse the fraction value to an integer that equals
            // [fraction value] * 10^[number of decimal places]
            uint.TryParse(strFraction, out mDenomenator);

            // get the greatest common divisor for both numbers
            uint gcd = GreatestCommonDivisor(mDenomenator, mNumerator);

            // divide the numerator and the denominator by the greatest common divisor
            mNumerator = mNumerator / gcd;
            mDenomenator = mDenomenator / gcd;
        }

        // create a string builder
        StringBuilder mBuilder = new StringBuilder();

        // add the whole number if it's greater than 0
        if (mWhole > 0m)
        {
            mBuilder.Append(mWhole);
        }

        // add the fraction if it's greater than 0m
        if (mFraction > 0m)
        {
            if (mBuilder.Length > 0)
            {
                mBuilder.Append(" ");
            }

            mBuilder.Append(mDenomenator);
            mBuilder.Append("/");
            mBuilder.Append(mNumerator);
        }

        return mBuilder.ToString();
    }

    // Converts fraction to decimal.
    // There are two formats a fraction greater than 1 can consist of
    // which this function will work for:
    //  Example: 4-1/2 or 4 1/2
    // Fractions less than 1 are in the format of 1/2, etc..
    public static decimal Convert(string value)
    {
        string[] dparse;
        string[] fparse;
        string whole = "0";
        string dec = "";
        decimal dReturn = 0;

        // check for '-' or ' ' separator between whole number and fraction
        dparse = value.Contains('-') ? value.Split('-') : value.Split(' ');
        int pcount = dparse.Count();

        // fraction greater than one.
        if (pcount == 2)
        {
            whole = dparse[0];
            dec = dparse[1];
        }
        // whole number or fraction less than 1.
        else if (pcount == 1)
            dec = dparse[0];

        // split out fractional part of value passed in.
        fparse = dec.Split('/');

        // check for fraction.
        if (fparse.Count() == 2)
        {
            try
            {
                decimal d0 = System.Convert.ToDecimal(fparse[0]); // convert numerator
                decimal d1 = System.Convert.ToDecimal(fparse[1]); // convert denominator
                dReturn = d0 / d1; // divide the fraction (converts to decimal)
                decimal dWhole = System.Convert.ToDecimal(whole); // convert
                                // whole number part to decimal.

                dReturn = dWhole + dReturn; // add whole number
                            // and fractional part and we're done.
            }
            catch (Exception e)
            {
                dReturn = 0;
            }
        }
        else
        // there is no fractional part of the input.
        {
            try
            {
                dReturn = System.Convert.ToDecimal(whole + dec);
            }
            catch (Exception e)
            {
                // bad input so return 0.
                dReturn = 0;
            }
        }

        return dReturn;
    }

    private static uint GreatestCommonDivisor(uint valA, uint valB)
    {
        // return 0 if both values are 0 (no GSD)
        if (valA == 0 &&
          valB == 0)
        {
            return 0;
        }
        // return value b if only a == 0
        else if (valA == 0 &&
              valB != 0)
        {
            return valB;
        }
        // return value a if only b == 0
        else if (valA != 0 && valB == 0)
        {
            return valA;
        }
        // actually find the GSD
        else
        {
            uint first = valA;
            uint second = valB;

            while (first != second)
            {
                if (first > second)
                {
                    first = first - second;
                }
                else
                {
                    second = second - first;
                }
            }

            return first;
        }

    }

    // Converts a value to feet and inches.
    // Examples:
    //  12.1667 converts to 12' 2"
    //  4 converts to 4'
    //  0.1667 converts to 2"
    public static bool ReformatForFeetAndInches
        (ref string line_type, bool zero_for_blank = true)
    {
        if (string.IsNullOrEmpty(line_type))
        {
            if (zero_for_blank)
                line_type = "0'";
            return true;
        }

        decimal d = System.Convert.ToDecimal(line_type);
        decimal d1 = Math.Floor(d);
        decimal d2 = d - d1;
        d2 = Math.Round(d2 * 12, 2);

        string s1;
        string s2;

        s1 = d1 == 0 ? "" : d1.ToString() + "'";
        s2 = d2 == 0 ? "" : FractionConverter.Convert(d2) + @"""";

        line_type = string.Format(@"{0} {1}", s1, s2).Trim();

        if (string.IsNullOrEmpty(line_type))
        {
            if (zero_for_blank)
                line_type = "0'";
            return true;
        }

        return true;
    }

    // Rounds a number to the nearest decimal.
    // For instance, carpenters do not want to see a number like 4/5.
    // That means nothing to them
    // and you'll have an angry carpenter on your hands
    // if you ask them cut a 2x4 to 36 and 4/5 inches.
    // So, we would want to convert to the nearest 1/16 of an inch.
    // Example: DecimalRound(0.8, 0.0625) Rounds 4/5 to 13/16 or 0.8125.
    private static decimal DecimalRound(decimal val, decimal places)
    {
        string sPlaces = FractionConverter.Convert(places, true);
        string[] s = sPlaces.Split('/');

        if (s.Count() == 2)
        {
            int nPlaces = System.Convert.ToInt32(s[1]);
            decimal d = Math.Round(val*nPlaces);
            return  d / nPlaces;
        }

        return val;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I've been a software developer for over 25 years. Worked on commercial grade Point of Sale and Order Entry software applications.

Worked with Symbol Technology Handheld bar code scanners quite a bit. Written Sqllite libraries that run on these devices.

Develop client/server solutions in ASP.NET. MVC, and MVVM design patterns.

Comments and Discussions

 
QuestionRun this code in Visual Studio 2005 Pin
Barrywl31-Aug-15 6:58
Barrywl31-Aug-15 6:58 
QuestionTest App for FractionConverter Pin
nbrooks11-Aug-13 5:12
professionalnbrooks11-Aug-13 5:12 
GeneralMy vote of 2 Pin
Philippe Mori19-Jul-13 13:03
Philippe Mori19-Jul-13 13:03 
GeneralRe: My vote of 2 Pin
Patrick Harris19-Jul-13 15:28
Patrick Harris19-Jul-13 15:28 
GeneralRe: My vote of 2 Pin
Garth J Lancaster19-Jul-13 17:49
professionalGarth J Lancaster19-Jul-13 17:49 
GeneralRe: My vote of 2 Pin
Patrick Harris20-Jul-13 5:45
Patrick Harris20-Jul-13 5:45 
GeneralRe: My vote of 2 Pin
Garth J Lancaster20-Jul-13 19:43
professionalGarth J Lancaster20-Jul-13 19:43 
GeneralRe: My vote of 2 Pin
Patrick Harris20-Jul-13 21:35
Patrick Harris20-Jul-13 21:35 
GeneralRe: My vote of 2 Pin
Philippe Mori23-Jul-13 14:31
Philippe Mori23-Jul-13 14:31 
GeneralRe: My vote of 2 Pin
Patrick Harris24-Jul-13 4:18
Patrick Harris24-Jul-13 4:18 
GeneralRe: My vote of 2 Pin
Philippe Mori24-Jul-13 4:48
Philippe Mori24-Jul-13 4:48 
GeneralRe: My vote of 2 Pin
Patrick Harris24-Jul-13 5:10
Patrick Harris24-Jul-13 5:10 
GeneralRe: My vote of 2 Pin
Philippe Mori24-Jul-13 5:38
Philippe Mori24-Jul-13 5:38 
GeneralRe: My vote of 2 Pin
Patrick Harris24-Jul-13 6:05
Patrick Harris24-Jul-13 6:05 
GeneralRe: My vote of 2 Pin
Philippe Mori24-Jul-13 6:21
Philippe Mori24-Jul-13 6:21 
GeneralRe: My vote of 2 Pin
Patrick Harris24-Jul-13 8:25
Patrick Harris24-Jul-13 8:25 
I'll take a look at this... thanks.
GeneralRe: My vote of 2 Pin
Philippe Mori24-Jul-13 8:31
Philippe Mori24-Jul-13 8:31 
GeneralRe: My vote of 2 Pin
Patrick Harris24-Jul-13 8:36
Patrick Harris24-Jul-13 8:36 
GeneralRe: My vote of 2 Pin
Philippe Mori24-Jul-13 9:09
Philippe Mori24-Jul-13 9:09 
GeneralRe: My vote of 2 Pin
Patrick Harris25-Jul-13 3:49
Patrick Harris25-Jul-13 3:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.