7

I'm having great difficulty with the following that I need to do for an assign:
a. Declare a data structure that contains a rational number.
b. Write f'xns that will +, -, *, / rational numbers.
All f'xns have to pass 3 parameters, each pointing to a data structure of the type I declared in part a; 2 of the parameters = operands, 3rd = result.
c. Write a f'xn that takes a pointer to your data structure as a parameter and returns the GCD of the numer. & denom.
d. Use your f'xn from part c to write a f'xn that will reduce a fraction (rational number) to lowest terms. Pass in a pointer to the fraction and have the fraction modified by the f'xn.
e. Write input and output functions so that a user can enter a fraction in the form 1/5 for example.

The user should be allowed to enter any number of problems, and the program should output the answer in lowest terms.

Am I on the right track? I believe I have a-c down, but not d and especially e. Can someone please guide me or help me correct my script?

int GCD (int numer, int denom)
{
    int result;
    while (denom > 0) {
        result = numer % denom;
        numer = denom;
        denom = result;
    }
    return numer;
}

int getLCM (int numer, int denom)
{
    int max;
    max = (numer > denom) ? numer : denom;
    while (1) {
        if (max % numer == 0 && max % denom == 0)
            break;
        ++max;
    }
    return max;
}

struct Fraction 
{
    int numer;
    int denom;
};

typedef struct 
{
    int numer;
    int denom; 
};
Fraction

Fraction add_fractions (Fraction a, Fraction b)
{
    Fraction sum;
    sum.numer = (a.numer * b.denom) + (b.numer * a.denom);
    sum.denom = a.denom * b.denom;
    return sum;
}

Fraction subtract_fractions (Fraction a, Fraction b)
{
    Fraction sum;
    sum.numer = (a.numer * b.denom) - (b.numer * a.denom);
    sum.denom = a.denom * b.denom;
    return sum;
}

Fraction multiply_fractions (Fraction a, Fraction b)
{
    Fraction sum;
    sum.numer = (a.denom * b.denom);
    sum.denom = (a.numer * b.numer);
    return sum;
}

Fraction divide_fractions (Fraction a, Fraction b)
{
    Fraction sum;
    sum.numer = (a.denom * b.numer);
    sum.denom = (a.numer * b.denom);
    return sum;
}

int main ()
{
    char response;

    printf ("FRACTION ARITHMETIC PROGRAM\n");
    printf ("Enter your problem (example 2/3 + 1/5):\n");
    scanf (, &problem);

    if (denom == 0 || denom < 0) {
        printf ("Illegal input!!\n");
        printf ("Another problem (y/n)?  ");
        scanf ("%c%*c", &response);
    } else {
        printf ("The answer is  ");

        printf ("Another problem (y/n)?  ");
        scanf ("%c%*c", &response);
    }

    while ((response == 'y') || (response == 'Y')) {
        printf ("\nWould you like to play again?\n");
        scanf ("%c%*c", &response);
    }

    while ((response == 'n') || (response == 'N'))
        printf ("Goodbye and thank you");

    return 0;
}

Edit after removing typedef thanks to comment responses:

struct Fraction {
    int numer;
    int denom;
};

struct Fraction add_fractions (struct Fraction a, struct Fraction b)
{
    struct Fraction sum;
    sum.numer = (a.numer * b.denom) + (b.numer * a.denom);
    sum.denom = a.denom * b.denom;
    return sum;
}

struct Fraction subtract_fractions (struct Fraction a, struct Fraction b)
{
    struct Fraction sum;
    sum.numer = (a.numer * b.denom) - (b.numer * a.denom);
    sum.denom = a.denom * b.denom;
    return sum;
}

struct Fraction multiply_fractions (struct Fraction a, struct Fraction b)
{
    struct Fraction sum;
    sum.numer = (a.denom * b.denom);
    sum.denom = (a.numer * b.numer);
    return sum;
}

struct Fraction divide_fractions (struct Fraction a, struct Fraction b)
{
    struct Fraction sum;
    sum.numer = (a.denom * b.numer);
    sum.denom = (a.numer * b.denom);
    return sum;
}
usuallystuck
  • 165
  • 2
  • 11
  • 2
    To my understanding, you are somewhat on the right track; however you could introduce a function to reduce the fractions to prevent the values in the numerator and denominator too quickly. – Codor Nov 24 '15 at 07:17
  • 8
    The type `struct Fraction` is wholly unrelated to the type `Fraction`, which is an anonymous (untagged) structure type with a typedef name of `Fraction`. You should probably combine the two, though either one could be used. What's erroneous is having both. – Jonathan Leffler Nov 24 '15 at 07:19
  • 1
    You can do better in `getLCM` function. It would be a good idea to normalize fraction before returning by diving numer and denom with gcd. (Write a function to normalize) Either `typedef` or `struct Fraction` can be removed to avoid confusion. – Mohit Jain Nov 24 '15 at 07:19
  • 4
    Note that the specification says that the operation functions will take three parameters — two (constant) pointers to the operands and a pointer to the result (and presumably the return type is `void`, though it could be a success/failure indicator showing whether the result could be stored). Your functions implement a more useful and usable scheme where the parameters are passed by value and the result is returned by value. However, that's not the spec. Beware of nitpicking teachers. You have similar issues with your GCD function; the specification says one interface, but you have another. – Jonathan Leffler Nov 24 '15 at 07:33
  • 1
    Additionally note that there were a number of statement nesting errors in the original code posted. The code has been reformatted to indent properly, which required correcting the nesting errors (e.g. mismatched `{ { }...`), but the original content was preserved. – David C. Rankin Nov 24 '15 at 07:37
  • Thanks, David! I removed typedef for now. In summary, I guess I'll need a fx'n that reduces the fractions, a better getLCM function, a fix for the GCD function fix, and a fix for the specific parameter requirements... Thanks, guys! – usuallystuck Nov 24 '15 at 07:49
  • Assuming 32-bit integers, when you add 1/100000 and 1/100000, your implementation will have an integer overflow, because (100000)^2 is more than a 32-bit `int` can handle. Yet the final result is 1/50000 which is representable in your implementation. – n. m. could be an AI Nov 24 '15 at 08:44
  • 4
    @Anonymissy, that is somewhat of a No-No... You are free to edit your question, but you need to paste new code *below* the original (or just include a statement at the end *Removed Code* and list the typedef) Why? Well look at Johnathan's comment now (the 2nd). It now makes no sense because the code it referred to in the Question is gone... Anybody else that comes along in the future to learn from the question will be left scratching his head wondering what the heck that comment is all about. Now it's not a killing offense, but some people are touchy about it. Just something you have to learn. – David C. Rankin Nov 24 '15 at 08:56
  • No worries, reverted the answer back to the original and added the edit below. Thanks for letting me know! – usuallystuck Nov 24 '15 at 09:15

3 Answers3

3

Some remarks with your code:

  • You do not thoroughly follow your requirements because GCD function takes 2 integers when it is required to take a pointer to struct, and your functions take 2 struct as parameter and return another one when they should take 3 (pointers to) structs.
  • Your GCD function uses a nice GCD implementation (thanks to Jonathan for its comment), even if some comment explaining the why would be nice for future readers
  • As you were said in comment, you should reduce rationals before doing operations on them to avoid unnecessary overflows, and when adding or subtracting rationals, you should use the LCM of the denoms for same reason
  • Your LCM algorithm is poor. As your GCD is nice why not simply use: LCM(a,b) = a * b / GCD(a,b) computed as lcm = (a/gcb) * b to reduce overflow risk (thanks to @n.m. for the simplified form)
  • Reduced form of a/b is a'/b' where a'=a/GCD(a,b) and b'=b/GCD(a,b)
  • What about a "%d/%d" format for both input and output, with the two members of a struct?

Last but not least, format "%c%*c" to get answer to a y/n question is possible but dangerous: you are likely to get the newline of preceding input into response! Choose one of line oriented input (with fgets + sscanf) or free form input (with scanf or fscanf) and stick to it. %1s into a char response[2] is much safer...

And carefully write in comment that you only process positive rationals or take care of sign! Such a detail can make users of a library rather angry ... not to mention nitpicking teachers (credits for Jonathan Leffler).

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • 1
    "why not simply use: LCM(a,b) = a * b / GCD(a,b) ". Because overflow. – n. m. could be an AI Nov 24 '15 at 09:33
  • @n.m.: the overflow problem is already taken into account in the end of the sentence. And if `lcm = (a/gcb) * (b/gcd) * gcd` overflows, nothing else can be done! – Serge Ballesta Nov 24 '15 at 09:37
  • Sorry, my attention span is restricted today... why not just `a / gcd(a,b) * b`? – n. m. could be an AI Nov 24 '15 at 09:40
  • 1
    As a point of detail, if you start the GCD with { 6, 4 } instead of { 4, 6 } (or vice versa), the first iteration effectively does the swap anyway and the result is correct. Try it. So that swap is not necessary; it clutters otherwise clean code at the cost of a division/modulus operation instead of a comparison. – Jonathan Leffler Nov 25 '15 at 15:59
2

You can use an enum for operators and a function to switch on the operators since all operators follow a similar pattern. This simplifies the code. Here is example of some of the implementations, you can add the rest:

typedef struct node {
    int nom;
    int denom;
} Tfraction;

typedef enum {PLUS, MINUS, MULTIPLY, DIVIDE} Ops;

int calculate(int x, Ops op, int y) {
    switch (op) {
        case PLUS: return x + y;
        case MINUS: return x - y;
        case MULTIPLY: return x * y;
        case DIVIDE: return x / y;
    }
}

//reccursive gcd
int gcdr (int a, int b) {
    if (a == 0) return b;
    return gcdr(b % a, a);
}

void simplify(Tfraction *fraction) {
    int gcd = gcdr(fraction->nom, fraction->denom);
    fraction->nom /= gcd;
    fraction->denom /= gcd;
}

Tfraction compute(Tfraction a, Tfraction b, Ops op) {
    if (op == DIVIDE) {
        int temp = b.nom;
        b.nom = b.denom;
        b.denom = temp;
        op = MULTIPLY;
    }

    if (op == MULTIPLY) {
        Tfraction result = { calculate(a.nom, op, b.nom), calculate(a.denom, op, b.denom) };
        simplify(&result);
        return result;
    }
    if (a.denom == b.denom) {
        Tfraction result = { calculate(a.nom, op, b.nom), a.denom };
        simplify(&result);
        return result;
    }
    else {
        Tfraction result = { (calculate((a.nom * b.denom), op, (b.nom * a.denom))), (a.denom * b.denom) };
        simplify(&result);
        return result;
    }
}

int main ()
{
    //Test
    Tfraction f1 = {2, 4}, f2 = {4, 2};

    printf("Addition: %d/%d\n", compute(f1, f2, PLUS).nom, compute(f1, f2, PLUS).denom);
    printf("Subtraction: %d/%d\n", compute(f1, f2, MINUS).nom, compute(f1, f2, MINUS).denom);
    printf("Multiplication: %d/%d\n", compute(f1, f2, MULTIPLY).nom, compute(f1, f2, MULTIPLY).denom);
    printf("Division: %d/%d\n", compute(f1, f2, DIVIDE).nom, compute(f1, f2, DIVIDE).denom);

    return 0;
}
Lukas
  • 3,423
  • 2
  • 14
  • 26
  • I don't think your simplify code is remotely valid. Consider a fraction presented as 15 / 9; you should end up with 5 / 3, but your code would yield 2 / 1, which is not the same thing at all. – Jonathan Leffler Nov 25 '15 at 16:04
  • I fixed it. there might be some improvements in the implementation but I was emphasizing on the logic. simplification isn't even in the question so you shouldn't down vote just for that – Lukas Nov 25 '15 at 16:19
  • Bullets (c) and (d) in the question are about simplifying fractions. Your 'fixed' code is still wrong. You need to be using GCD and/or LCM and you are not. You have also not attempted to follow the specification in the question w.r.t functions taking three pointers, one per operation. – Jonathan Leffler Nov 25 '15 at 17:09
  • Sorry about that, I fixed it. – Lukas Nov 25 '15 at 22:03
  • The simplify code still isn't correct. Given 48 / 12 to simplify, it divides by 2 and exits the loop, leaving 12 / 6 instead of dividing by another factor of 2 and a factor of 3 to leave 4 / 1. – Jonathan Leffler Nov 25 '15 at 23:35
  • No it doesn't give 12 / 6. run it again. It does give 4 / 1. It starts from the biggest dividers until it hits a common one for both denom and nominator so it won't loop until 2 in this case – Lukas Nov 26 '15 at 07:10
  • I stand corrected — it does yield the correct result. However, it is not as efficient as using a GCD calculation. For example, if the fraction is 36/33, it will attempt 62 modulo operations before finding that 3 is the common divisor. Using GCD, that would be 3 modulo operations. Euclid was pretty clever when he discovered and recorded the GCD algorithm 2300-odd years ago. – Jonathan Leffler Nov 26 '15 at 07:36
  • You're right, I didn't know about that. Thanks I changed it ;) – Lukas Nov 27 '15 at 13:09
0

I changed the sum and multiply functions to minimize overflow, although I did not change them to accept 3 arguments (I prefer this way).

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int numer;
    int denom; 
} Fraction;

int gcd(int numer, int denom) {
  int result;
  while (denom != 0) {
    result = numer % denom;
    numer = denom;
    denom = result;
  }
  return numer;
}

int lcm(int a, int b) {
  return (a / gcd(a,b)) * b;
}

Fraction simplify (Fraction a) {
  int cd;
  cd = gcd(a.numer, a.denom);
  a.numer /= cd;
  a.denom /= cd;
  return a;
}

Fraction add_fractions (Fraction a, Fraction b) {
  Fraction sum;
  int lcmd;
  a = simplify(a);
  b = simplify(b);
  lcmd = lcm(a.denom, b.denom);
  sum.numer = (lcmd / a.denom * a.numer) + (lcmd / b.denom * b.numer);
  sum.denom = lcmd;
  return simplify(sum);
}

Fraction subtract_fractions (Fraction a, Fraction b) {
  Fraction sum;
  int lcmd;
  a = simplify(a);
  b = simplify(b);
  lcmd = lcm(a.denom, b.denom);
  sum.numer = (lcmd / a.denom * a.numer) - (lcmd / b.denom * b.numer);
  sum.denom = lcmd;
  return simplify(sum);
}

void swap(int *a, int *b) {
  int tmp = *a;
  *a = *b;
  *b = tmp;
}

Fraction multiply_fractions (Fraction a, Fraction b) {
  Fraction sum;
  a = simplify(a);
  b = simplify(b);
  swap(&a.numer, &b.numer); // another round of simplifications to avoid (minimize) overflows below
  a = simplify(a);
  b = simplify(b);
  sum.numer = (a.numer * b.numer);
  sum.denom = (a.denom * b.denom);
  return sum;
}

Fraction divide_fractions (Fraction a, Fraction b) {
  swap(&b.numer, &b.denom);
  return multiply_fractions(a, b);
}

int main() {
  int a, b;
  Fraction f1 ,f2, res;

  printf("gcd(12,9)=%d\n", gcd(12,9));
  printf("gcd(9,12)=%d\n", gcd(9,12));

  printf("gcd(4,12)=%d\n", gcd(4,12));

  printf("gcd(8,12)=%d\n", gcd(8,12));
  printf("gcd(12,8)=%d\n", gcd(12,8));

  puts("-");

  printf("lcm(12,9)=%d\n", lcm(12,9));
  printf("lcm(9,12)=%d\n", lcm(9,12));

  printf("lcm(8,12)=%d\n", lcm(8,12));
  printf("lcm(12,8)=%d\n", lcm(12,8));

  printf("lcm(4,12)=%d\n", lcm(4,12));
  printf("lcm(3,5)=%d\n", lcm(3,5));
  printf("lcm(4,5)=%d\n", lcm(4,5));
  printf("lcm(3,4)=%d\n", lcm(3,4));

  puts("-");

  f1.numer = 12;
  f1.denom = 9;
  printf(" %d/%d simplified to", f1.numer, f1.denom);
  f1 = simplify(f1);
  printf(" %d/%d \n", f1.numer, f1.denom);

  f1.numer = 8;
  f1.denom = 12;
  printf(" %d/%d simplified to", f1.numer, f1.denom);
  f1 = simplify(f1);
  printf(" %d/%d \n", f1.numer, f1.denom);

  puts("-");

  f1.numer = 1; f1.denom = 4;
  f2.numer = 1; f2.denom = 4;
  res = add_fractions(f1, f2);
  printf(" %d/%d + %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

  f1.numer = 1; f1.denom = 4;
  f2.numer = 1; f2.denom = 12;
  res = add_fractions(f1, f2);
  printf(" %d/%d + %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

  f1.numer = 1; f1.denom = 3;
  f2.numer = 5; f2.denom = 6;
  res = add_fractions(f1, f2);
  printf(" %d/%d + %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

  f1.numer = 35; f1.denom = 100;
  f2.numer = 1; f2.denom = 4;
  res = subtract_fractions(f1, f2);
  printf(" %d/%d - %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

  f1.numer = 7; f1.denom = 10;
  f2.numer = 1; f2.denom = 2;
  res = subtract_fractions(f1, f2);
  printf(" %d/%d - %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

  f1.numer = 1; f1.denom = 2;
  f2.numer = 1; f2.denom = 2;
  res = multiply_fractions(f1, f2);
  printf(" %d/%d x %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

  f1.numer = 12; f1.denom = 5;
  f2.numer = 5; f2.denom = 6;
  res = multiply_fractions(f1, f2);
  printf(" %d/%d x %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

  f1.numer = 12; f1.denom = 21;
  f2.numer = 7; f2.denom = 4;
  res = multiply_fractions(f1, f2);
  printf(" %d/%d x %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

  f1.numer = 1; f1.denom = 5;
  f2.numer = 1; f2.denom = 5;
  res = divide_fractions(f1, f2);
  printf(" %d/%d / %d/%d = %d/%d \n", f1.numer, f1.denom, f2.numer, f2.denom, res.numer, res.denom);

}
Bernardo Ramos
  • 4,048
  • 30
  • 28