9

I am trying to take everything after the decimal and display it as a fraction. Couldn't find much for objective-c on how to accomplish this. I am using double for my formatting of variables, not sure if that would matter or not. This is how I am formatting for output of my answer:[theTextField setText:[NSString stringWithFormat:@"%f''", (myVariable)]]; This displays ok as decimal, but would really like it as a whole number and fraction (ie.) 7 1/2 instead of 7.5000. Thank you in advanced!

Update:5/13/2011

Well, I got it to display 7 1/16, but something with the math is off. Because it won't change from 1/16 even by changing the values that are getting divided. Where am I going wrong here? If anyone can actually get this to work correctly please post fully how its done. This is something that should be simple but isn't and way too time consuming. Please post fully how it is done. Thanks.

Update: If this answer isn't working for you check out my other post, this works! Convert decimals to fractions

Community
  • 1
  • 1
Jason
  • 650
  • 2
  • 10
  • 33

2 Answers2

8

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?

Community
  • 1
  • 1
Regexident
  • 29,441
  • 10
  • 93
  • 100
  • Have you tried using this? It gives 4 warnings and crashes the app, not sure what needs to be fixed. This would go in the .m file right? – Jason Apr 13 '11 at 13:37
  • Works for me. Try replacing `"main(ac, av) int ac; char ** av; {"` with `"int main (int ac, const char * av[]) {"` and it should work just fine. – Regexident Apr 13 '11 at 14:45
  • Got it working by changing the name from `main` to `convert` and added `return 0;` to the end. Thanks – Jason Apr 16 '11 at 12:56
  • What is the best way to call on this function? Within the actual math of my code, or when displaying answer to textfield with the above code? – Jason Apr 16 '11 at 13:00
  • That pretty much depends on what you're doing with the fraction afterwards. Converting reals to fractions is not always lossless. (as the output of my sample output shows) Thus you should in general do the conversion as the very last step before displaying to ensure best accuracy. Btw, I added a quick function wrapper to my answer for convenience. – Regexident Apr 16 '11 at 16:36
  • I'm just displaying the the fraction in a textfield after converting, that's it. Above is the way I'm displaying the answer as a decimal, how could I call it within the code above? – Jason Apr 18 '11 at 21:24
  • See the just added tail of my answer on function integration. Good luck with it! – Regexident Apr 18 '11 at 21:55
  • So with the code to put it in a textbox, I wouldn't need the "calling it" code since that would be calling it too? Also, your example `long maxDenominator = 1000;` is just a example, so I would replace `1000` with `myvarible` right? Sorry but I'm very confused... – Jason Apr 21 '11 at 12:20
  • All you'd nee in your own project is the very last code snippet. And maxDenominator (just like the name implies) is the largest denominator that's allowed for your resulting fraction. it's totally up to you to choose a value that fits your needs. – Regexident Apr 21 '11 at 20:57
  • It's taking the entire value and making it a fraction, not what is after the decimal. – Jason Apr 22 '11 at 15:47
  • Well, how about you do a little math, then? If myValue is 7.6543, then split it into 7 and 0.6543 (via a cast to NSInteger) and just calculate the fraction for the latter. It's not rocket science after all, is it? C'mon and do a little thinking of your own. ;) – Regexident Apr 22 '11 at 15:59
  • Apologies, I'm still very new at this. This is my first app and learning as best as I can. I've learned a lot so far...I know how to do the math on a calculator or on paper, but putting it in code is a new ballgame for me. Thanks for your help. – Jason Apr 22 '11 at 18:18
  • I guess this would be the best place to ask rather than posting another question to help others.I asked how to get 7.5 for example to display as 7 1/2. You showed me how I can take the entire value and display as a fraction, which wasn't really what I was asking. I understand this is something that's easy for you, But it's only easy if you know how, which is why I'm asking and the point of this entire site. If you could continue to explain how this is done, (casting to NSInteger) in order to get the answer I was looking for, it would be greatly appreciated!! Thank you! – Jason Apr 26 '11 at 12:54
  • Jason, I updated my answer. Please see the last (updated) code snippet. And check the link for info regarding loss of precision. – Regexident Apr 26 '11 at 13:29
  • I have my maxdonominator as 16, that always stays the same, but my nominator is always 1. so no matter what my variable is my fraction never changes from 1/16. Any idea whats happening or wrong? – Jason May 13 '11 at 13:33
  • I think you have missed something when you integrated David Eppstein's code, which computes *two* approximations at the end with different error margins. You should use the smallest, instead you always use the first. For instance, try calling this code with 0; you will get 1/4 whereas you should get 0/1, of course. Also, be aware that even when you correct this there are some edge cases that this code does not handle, as I have just pointed out on [link]http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions. – edsko Aug 01 '11 at 08:38
7

I have written code to convert decimal to its lowest possible fraction. This works perfectly well.

-(int)gcdForNumber1:(int) m andNumber2:(int) n 
{
    while( m!= n) // execute loop until m == n
    {
        if( m > n)
            m= m - n; // large - small , store the results in large variable<br> 
        else
            n= n - m;
    }
    return ( m); // m or n is GCD
}


-(int)tenRaisedTopower:(int)decimalLength { 
    int answer = 10; 
    while (decimalLength!= 1) {
        answer *= 10;
        decimalLength -- ; 
    } 
    return answer;
}

-(void)floatToFraction:(float)decimalNumber 
{
    NSString *decimalString = [NSString stringWithFormat:@"%f", decimalNumber];
    NSArray *components = [decimalString componentsSeparatedByString:@"."];
    int decimalLength = [[components objectAtIndex:1] length];
    int n = [self tenRaisedTopower:decimalLength];
    int m = [[components objectAtIndex:1] intValue];
    int gcd = [self gcdForNumber1:m andNumber2:n];
    int numer = m/gcd;
    int deno = n/gcd;
    int fractionnumer = ([[components objectAtIndex:0] intValue] * deno) + numer;
    NSLog(@"answer>>%d/%d", fractionnumer, deno);
}

call method as:

[self floatToFraction:2.5];  
Duck
  • 34,902
  • 47
  • 248
  • 470
JJQ
  • 79
  • 1
  • 2
  • You should familiarize yourself with StackOverflow code formatting: http://stackoverflow.com/editing-help. Of particular note, you don't need to use HTML `
    ` tags for newlines. Look at how I edited your answer.
    –  Nov 09 '11 at 07:48
  • This has an issue when you don't use positive integers. The modular method should be used for any integers. - (int)gcdForNumber1:(int) m andNumber2:(int) n { while( n!=0) { int temp = n; n = m % temp; m = temp; } return (m); } – Paul Solt May 10 '13 at 17:44