none
decimal or double to mixed fraction in C# RRS feed

  • Question

  • Dear team,

             i want to convert double to fraction mixed fraction.

    eg  MessageBox.Show(DoubleToFraction(27.523869));

     

    public static string DoubleToFraction(double num, double epsilon = 0.0001, int maxIterations = 100) { double[] d = new double[maxIterations + 2]; d[1] = 1; double z = num; double n = 1; int t = 1; int wholeNumberPart = (int)num; double decimalNumberPart = num - Convert.ToDouble(wholeNumberPart); while (t < maxIterations && Math.Abs(n / d[t] - num) > epsilon) { t++; z = 1 / (z - (int)z); d[t] = d[t - 1] * (int)z + d[t - 2]; n = (int)(decimalNumberPart * d[t] + 0.5); } return string.Format((wholeNumberPart > 0 ? wholeNumberPart.ToString() + " " : "") + "{0}/{1}", n.ToString(), d[t].ToString() ); }

    in excel the answer is "27 417/796" but i am not geting the result.

    for 40.1242236024845 i have to get "40 20/161" 

    kindly help me..

    Monday, July 23, 2018 1:26 PM

Answers

  • using System;
    using System.Collections.Generic;
    using System.Linq;
    
    public class Program
    {
      public static void Main()
      {
        Func<double,int,string>d2s=(nmbr,mxDvsr)=>
        {
          var f=Math.Abs(nmbr-Math.Truncate(nmbr));
          if(f<=1.0/mxDvsr)return $"{nmbr}: NA";
          var num=Math.Abs(nmbr);
          int i=(int)Math.Truncate(nmbr),d2=0,d1=1,d;
          do(d2,d1)=(d1,(d=d1*(int)(num=1/(num-(int)num))+d2));while(Math.Abs(d)<mxDvsr);
          return$"{i}: {(int)(f*d2+0.5)}/{d2}";
        };
        
        const int maxDivisor=1_000; // const int maxDivisor=1_000_000;
        new List<double>{
          -24.000228
          ,-24.052228
          ,27.523869
          ,40.124223
          ,40.1242236024845
        }
        .ForEach(d=>Console.WriteLine($"{d}\n\t\t{d2s(d,maxDivisor)}"))
        ;
      }
    }
    
    /*
    The output for maxDivisor=1_000
    
    -24.000228
    		-24.000228: NA
    -24.052228
    		-24: 34/651
    27.523869
    		27: 417/796
    40.124223
    		40: 20/161
    40.1242236024845
    		40: 20/161
    
    The output for maxDivisor=1_000_000
    
    -24.000228
    		-24: 57/250000
    -24.052228
    		-24: 13057/250000
    27.523869
    		27: 111988/213771
    40.124223
    		40: 15368/123713
    40.1242236024845
    		40: 20/161
    */

    Do you see any inconsistency?

    Notice how small the code is... 

    Don't bother with the format; just copy and paste into an advanced IDE&Compiler.


    • Edited by ritehere40Banned Thursday, July 26, 2018 12:28 AM some words missing
    • Marked as answer by mann madhan1 Thursday, July 26, 2018 5:54 AM
    Thursday, July 26, 2018 12:20 AM

All replies

  • Where did you get your algorithm? Excel doesn't allow arbitrary fractional conversions. Beyond some standard numbers it only goes to 3 digits of fractions. This site is useful for coming up with math formulas and it provides calculators as well. Part of this depends upon whether you want accurate vs approximate. Accurate is good in some cases but approximates generally look better. In your scenario the fractions you are coming up with evaluate to the correct decimals. So what issue are you having?

    Michael Taylor http://www.michaeltaylorp3.net

    Monday, July 23, 2018 3:11 PM
    Moderator
  • A different approach which seems to work well. I am not sure quite how many digits it will handle, but it seems to be quite a few.

    I have not clue about performance....

        public static string DoubleToFraction(double num, int maximumNumberOfDecimalsToConsider)
            {
                int wholeNumberPart = (int)num;
                double decimalNumberPart = num - Convert.ToDouble(wholeNumberPart);
                long denominator = (long)Math.Pow(10, maximumNumberOfDecimalsToConsider);
                long numerator = (long)(decimalNumberPart * (double)denominator);
                var fraction = ReduceFraction(numerator, denominator);
                return string.Format("{0} : {1}/{2}", wholeNumberPart, fraction.Item1, fraction.Item2);
    
            }
            public static Tuple<long, long> ReduceFraction(long numerator, long denominator)
            {
                Tuple<long, long> numDen = new Tuple<long, long>(numerator, denominator);
                if (numerator % denominator == 0)
                {
                    numDen = new Tuple<long, long>(1, denominator/numerator);
                }
                else
                {
                    int maximumPossibleFactor = (int)Math.Min(Math.Sqrt(numerator), Math.Sqrt(denominator));
                    for (int possibleFactor = 2; possibleFactor <= maximumPossibleFactor; possibleFactor++)
                    {
                        if (numerator % possibleFactor == 0 && denominator % possibleFactor == 0)
                        {
                            numDen = ReduceFraction(numerator / possibleFactor, denominator / possibleFactor);
                        }
                    }
                }
                return numDen;
    
            }
    Ethan


    Ethan Strauss

    Monday, July 23, 2018 4:13 PM
  • Dear ethan,

    MessageBox.Show(DoubleToFraction(27.523869,3));

    your function is returning "27 : 523/1000"

    Tuesday, July 24, 2018 4:03 AM
  • Dear ethan,

    MessageBox.Show(DoubleToFraction(27.523869,3));

    your function is returning "27 : 523/1000"

    27 : 523/1000 is the correct answer; its just that Ethan's sub-method to reduce the fraction isn't working (though wouldn't make a difference in this case, since 523 is a prime number).

    I changed Ethan's code by stealing a 'greatest common denominator' function and using that instead:

    public static string DoubleToFraction(double num, int maximumNumberOfDecimalsToConsider)
    {
        int wholeNumberPart = (int)num;
        double decimalNumberPart = num - Convert.ToDouble(wholeNumberPart);
        long denominator = (long)Math.Pow(10, maximumNumberOfDecimalsToConsider);
        long numerator = (long)(decimalNumberPart * (double)denominator);
        long gcd;
        GreatestCommonD(ref numerator, ref denominator, out gcd);
        return string.Format("{0} : {1}/{2}", wholeNumberPart, numerator, denominator);
    
    }
    static void GreatestCommonD(ref long Numerator, ref long Denominator, out long greatestCommonD)
    {
        greatestCommonD = 0;
        for (int x = 1; x <= Denominator; x++)
        {
            if ((Numerator % x == 0) && (Denominator % x == 0))
                greatestCommonD = x;
        }
        if (greatestCommonD == 0)
        {
            return;
        }
        else
        {
            Numerator = Numerator / greatestCommonD;
            Denominator = Denominator / greatestCommonD;
        }
    }


    • Edited by RJP1973 Tuesday, July 24, 2018 8:07 AM
    Tuesday, July 24, 2018 8:01 AM
  • Dear RJP,

           In excel the answer is "27 417/796" but the function is returning "27 : 523/1000". what fumula Microsoft excel is using. 

    Tuesday, July 24, 2018 11:24 AM
  • Dear RJP,

           In excel the answer is "27 417/796" but the function is returning "27 : 523/1000". what fumula Microsoft excel is using. 

    That is good question and one I cannot immediately answer (but I will continue to ponder it).

    If you actually calculate 417/796 you get 0.52386934673366834170854271356784 (to the limits of Calc). Which is obviously not your input of 0.523869.

    My guess is its giving you the lowest fraction it can while approximating the input to a certain point, rather than giving you the fraction that exactly equals your input but has a larger numerator.

    As an aside, to explain the results myself and others have posted here (if you're not sure how our results come about), then consider

    • 0.5 ≡ 5/10, reducible to 1/2
    • 0.52 ≡ 52/100, reducible to 13/25
    • 0.523 ≡ 523/1000, not reducible as 523 is prime.


    • Edited by RJP1973 Tuesday, July 24, 2018 11:52 AM
    Tuesday, July 24, 2018 11:38 AM
  • Thanks RJP,

               waiting for your reply

    Tuesday, July 24, 2018 11:52 AM
  • Hi,

     I think that the algorithm you need will really depend on how you will handle rounding issues.

    Note that my algorithm when called as

    DoubleToFraction(27.523869, 6);

    returns 27 : 523869/1000000 which is exactly the input. It can get arbitrarily close (to many digits) to any input.

    Why do you need a fraction? How exact does the value need to be? If the goal is to mimic Excel, then the question is: What algorithm does Excel use? If that's what you need to know, an Excel forum is probably the best place to ask.

    Ethan

    ps. RJP. My reduce fraction function is working for me. Can you describe how it is breaking for you?


    Ethan Strauss

    Tuesday, July 24, 2018 2:31 PM
  • @Ethan

    When I tried DoubleToFraction(27.523869, 1) it returned 5/10 rather than 1/2.

    I didn't try it much further so maybe its just a base case that's missing somewhere.

    Tuesday, July 24, 2018 4:26 PM
  • Thanks RJP. I didn't try the simplest cases (1 decimal).

    Should be

      if (denominator % numerator == 0)  

    not

    if (numerator % denominator == 0)

    Ethan


    Ethan Strauss

    Tuesday, July 24, 2018 4:34 PM
  • Dear Ethan,

     I am not minic excel

      my inputs is =  1/ ((1/785 *16)+(1/242 * 5)) =24.052228

    the result in Excel is "24  34/651" but the function returning is : 24. 13057/250000


    Wednesday, July 25, 2018 4:18 AM
  • By my calculation (using Excel)

    24  13057/250000= 24.0522280000000

    24  34/651 =           24.0522273425499...

    These numbers are almost the same, but my calculation is exactly equal to your input, but the answer you report from Excel is off by about 0.0000007.

    I am not clear on what the issue is. Is this difference between values too large?

    Ethan


    Ethan Strauss

    Wednesday, July 25, 2018 3:23 PM