Convert decimal to fraction in Objective-C?

Objective-C uses pure C for basically all primitive mathematical operations.

This being said you’ll find all necessary information (along with C code) in the answers of this other question:

How to convert floats to human-readable fractions?

(Specifically this answer featuring actual C code.)

Here is a quick c function wrapper of said algorithm:

typedef struct {
    long nominator;
    long denominator;
    double error;
} Fraction;

/*
 * Find rational approximation to given real number
 * David Eppstein / UC Irvine / 8 Aug 1993
 *
 * With corrections from Arno Formella, May 2008
 * Function wrapper by Regexident, April 2011
 *
 * usage: fractionFromReal(double realNumber, long maxDenominator)
 *   realNumber: is real number to approx
 *   maxDenominator: is the maximum denominator allowed
 *
 * based on the theory of continued fractions
 * if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
 * then best approximation is found by truncating this series
 * (with some adjustments in the last term).
 *
 * Note the fraction can be recovered as the first column of the matrix
 *  ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
 *  ( 1  0 ) ( 1  0 ) ( 1  0 )
 * Instead of keeping the sequence of continued fraction terms,
 * we just keep the last partial product of these matrices.
 */
Fraction fractionFromReal(double realNumber, long maxDenominator) {
   double atof();
   int atoi();
   void exit();

   long m[2][2];
   double startx;
   long ai;

   startx = realNumber;

   // initialize matrix:
   m[0][0] = m[1][1] = 1;
   m[0][1] = m[1][0] = 0;

   // loop finding terms until denom gets too big:
   while (m[1][0] *  (ai = (long)realNumber) + m[1][1] <= maxDenominator) {
       long t;
       t = m[0][0] * ai + m[0][1];
       m[0][1] = m[0][0];
       m[0][0] = t;
       t = m[1][0] * ai + m[1][1];
       m[1][1] = m[1][0];
       m[1][0] = t;

       if (realNumber == (double)ai) {
           // AF: division by zero
           break;
       }

       realNumber = 1 / (realNumber - (double)ai);

       if (realNumber > (double)0x7FFFFFFF) {
           // AF: representation failure
           break;
       }
   }

   ai = (maxDenominator - m[1][1]) / m[1][0];
   m[0][0] = m[0][0] * ai + m[0][1];
   m[1][0] = m[1][0] * ai + m[1][1];
   return (Fraction) { .nominator = m[0][0], .denominator = m[1][0], .error = startx - ((double)m[0][0] / (double)m[1][0]) };
}

Calling it like this:

double aReal = 123.45;
long maxDenominator = 42;
Fraction aFraction = fractionFromReal(aReal, maxDenominator);
printf("Real %.3f -> fraction => %ld/%ld, error: %.3f\n",
       aReal,
       aFraction.nominator,
       aFraction.denominator,
       aFraction.error);

Prints this:

Real 123.450 -> fraction => 3827/31, error: -0.002

Last but not least let’s see how we get our newly crafted fraction into out text field:

double myVariable = 7.5;
long maxDenominator = 1000; //sample value
Fraction myFraction = fractionFromReal(abs(myVariable - (NSInteger)myVariable), maxDenominator);
[theTextField setText:[NSString stringWithFormat:@"%d %d/%d", (NSInteger)myVariable, myFraction.nominator, myFraction.denominator]];

Expected output: "7 1/2", actual output: "7 499/999"
For some info on why this can happen see this answer to a related question: How to convert floats to human-readable fractions?

Leave a Comment