Let’s develop a solid foundation in procedural/structured programming by solving a mixed fraction arithmetic calculator case study.

Case study: Mixed fraction arithmetic calculator

We’ll tackle our case study by breaking it down into manageable tasks, called functions, and then use them to write the complete flow (the sequence) of the program. We’ll discuss each step of the coding process and learn how to derive other arithmetic operations from the structure of our addition function. Let’s solve this case study!

We already know how to compute arithmetic operations on mixed fractions mathematically (as we did in the “Chapter Overview” lesson). How can we implement it in code?

Finding out the memory requirement

First, we need something that can store our mixed fraction and let us access its individual components (whole number, numerator, and denominator) to perform our arithmetic operations.

For that, we’ll use variables as shown below:

int w; //whole number
int n; //numerator
int d; //denominator

We declare three int variables (w, n, and d) for the whole number, numerator, and denominator, respectively. For the entire calculator (testing program), we need three fractions. Let’s classify them as left and right fractions for simplicity, where the variables starting with l_ represent the left fraction's components and r_ the right. The result of the addition, subtraction, multiplication, and division will be stored inside another final fraction, which has three more variables starting with f_.

We’ll use these three fractions in our main() function and later pass them to the functions for arithmetic operations (addition, subtraction, multiplication, and division).

int f_w, f_n, f_d; //Final fraction
int l_w, l_n, l_d; //Left fraction
int r_w, r_n, r_d; //Right fraction

To get started with the calculator, let’s begin by implementing the addition operation.

Implementing the add() function

Since we want to compute the sum of two mixed fractions, we need six variables as parameters or operands. Here’s the prototype of the add() function:

void add(int l_w, int l_n, int l_d, // left fraction
int r_w, int r_n, int r_d, // right fraction
int &f_w, int &f_n, int &f_d);// final fraction

Notice the extra three int variables (&f_w, &f_n, and &f_d)?

The add() function receives three variables by reference (preceded by &), meaning any modifications made to these variables within the function will be reflected in the variables passed as arguments. Specifically, the last three variables passed to the function will hold the resulting sum of the left and right fractions. Since the function can’t return more than one value, we use this approach to update the value of our final fraction components in the context where the function is called.

Flow of the add() function

For a holistic view of what we eventually want to do in this add() function, the following code snippet is given, showing all the steps translated from the mathematical steps we learned in the “Chapter Overview” lesson for adding two mixed fractions.

void add(int l_w, int l_n, int l_d,
int r_w, int r_n, int r_d,
int &f_w, int &f_n, int &f_d)
{
/*
Step 1:Convert the two fractions L and R to improper fraction
void improper_fraction(int w, int n, int d, int &new_n, int &new_d);
Example: w = 2, n = 3, d = 4,
The function will store in: new_n = 11, new_d = 4
*/
int new_l_n, new_l_d;
int new_r_n, new_r_d;
improper_fraction(l_w, l_n, l_d, new_l_n, new_l_d);
improper_fraction(r_w, r_n, r_d, new_r_n, new_r_d);
//Addition Operations//
/*
Step 2: Make same denominator for both fraction
void same_denominator(int &new_l_n, int &new_l_d,
int &new_r_n, int &new_r_d);
new_l_n = 2, new_l_d = 3 --> new_l_n = 4, new_l_d = 6
new_r_n = 1, new_r_d = 2 --> new_r_n = 3, new_r_d = 6
*/
same_denominator(new_l_n, new_l_d, new_r_n, new_r_d);
/*
Step 3: Computing the resultant fraction
new_l_n = 4, new_l_d = 6
new_r_n = 3, new_r_d = 6
f_w = 0;
f_n = 4 + 3 = 7;
f_d = 6;
*/
f_w = 0, f_n = new_l_n + new_r_n, f_d = new_l_d;
/*
Step 4: Reducing the Resultant fraction (GCD)
f_n = 8;
f_d = 6; GCD = 2
f_n = 8/2 = 4; and f_d = 6/2 = 3;
*/
int gcd = GCD(f_n, f_d);
f_n /= gcd, f_d /= gcd;
//Step 5: Convert back to Mixed Fraction & Print
// Given: void mixed_fraction(int &f_w, int &f_n, int &f_d);
mixed_fraction(f_w, f_n, f_d);
}
The flow of the add() function

We don’t need to understand the code yet; we’ll return to it later. For now, let’s try to understand its flow by paying attention to parameters, arguments, and the return types of the functions. Let’s look at each of these steps in detail.

Step 1: Convert the given mixed fractions into improper fractions

As we’ve sent two mixed fractions (one for the left and one for the right) to the add() function, converting them into improper fractions is necessary before we can carry out the addition operation. Therefore, our initial mathematical step will be to convert them into improper fractions.

Here’s the function for the conversion from a mixed to an improper fraction:

void improper_fraction(int w, int n, int d, int &new_n, int &new_d)
{
// 1: Calculating the new numerator of the fraction
int temp_numerator = improper_fraction_numerator(w, n, d); //calculates the new numerator
// 2: Assigning a sign to the fraction
temp_numerator *= compute_sign(w, n, d); //Assigns sign
// 3: Assigning the denominator value
int temp_denominator = d; //denominator for both fraction remains the same
new_n = temp_numerator;
new_d = temp_denominator;
return;
}

The improper_fraction() has a total of five parameters. The first three (int w, int n, and int d) are the components of the mixed fraction sent to be converted, whereas int &new_n and int &new_d are the improper fraction components sent by reference that need to be updated.

Note: There is no whole number variable for improper fraction. Hence, only two variables (new_n and new_d) for numerator and denominator, respectively, are passed.

There are a total of three steps involved in this conversion:

  1. Calculating the new numerator of the fraction

  2. Assigning a sign to the fraction

  3. Assigning a denominator value

1. Calculating the numerator

This is achieved using the improper_fraction_numerator() helper function, given below:

int improper_fraction_numerator(int w, int n, int d)
{
return abs(d) * abs(w) + abs(n);
}

This helper function calculates and returns the new numerator of the improper fraction. Notice how we used the abs() function for each component before calculating the new numerators. The abs() function returns the absolute value of its parameter.

2. Assigning the fraction sign

After calculating the new numerator, it is important to reassign the sign to the fraction, as the new numerator is computed based on the absolute values of the components. So, to do that, we use the compute_sign() helper function as shown in the following code:

int compute_sign(int w, int n, int d)
{
int sign_w, sign_n, sign_d;
if (w <0)
sign_w = -1;
else
sign_w = 1;
if (n <0)
sign_n = -1;
else
sign_n = 1;
if (d <0)
sign_d = -1;
else
sign_d = 1;
return sign_w * sign_n * sign_d;
}

The sign is associated with the whole fraction, not its components. So, we need to normalize each component and compute its overall sign. Imagine, in any fraction, if we have a negative sign in the whole number and the numerator. What do you think would be the overall fraction sign?

Negatives will cancel out to give a positive sign to the overall fraction, a simple mathematical formula. So, whenever we need to compute the overall sign of the fraction, we’ll call the compute_sign() function just like we did in the improper_fraction() function.

We pass the final numerator and denominator to the compute_sign() function, which returns the resultant sign bit for the fraction to be multiplied by temp_numerator. The code for this function is given below:

temp_numerator *= compute_sign(w, n, d);
3. Assigning the fraction denominator

Since the denominator for the improper fraction remains the same as the mixed fraction, we already have our improper fraction: new_n / d.

int temp_denominator = d; //denominator for both fractions remains the same

Note: We didn't include the reduction of the fraction step while calculating the improper fraction. We'll return to it after we have a final output of the addition operation.

Now that we have a function that can convert mixed fractions to improper ones, we’ll call it for both left and right fractions.

int new_l_n, new_l_d; //left fraction
int new_r_n, new_r_d; //right fraction
improper_fraction(l_w, l_n, l_d, new_l_n, new_l_d);
improper_fraction(r_w, r_n, r_d, new_r_n, new_r_d);

Here, new_l_n and new_l_d are the new numerator and denominator for left, respectively, and new_r_n and new_r_d are the new numerator and denominator for the right fraction.

Since we’ve got our fractions converted into the improper form, let’s move to the next step: making the denominators the same for the two fractions.

Step 2: Making the denominators the same

We need to update the denominators to add improper fractions. To achieve that, we write the following helper function:

//Making d same - Step 1
void same_denominator(int &new_l_n, int &new_l_d, int &new_r_n, int &new_r_d){
// 1: left n = left numerator * right denominator
int temp_l_n = new_l_n * new_r_d;
// 2: left n = right numerator * left denominator
int temp_r_n = new_r_n * new_l_d;
// 3: Denominator calulation
int temp_d = new_l_d * new_r_d;
// 4: Assigning values
new_l_n = temp_l_n, new_l_d = temp_d;
new_r_n = temp_r_n, new_r_d = temp_d;
return;
}

We pass components of both improper fractions by reference to this function because we want to update multiple variables. We update the left fraction’s numerator new_l_n by multiplying it with the denominator of the right fraction, new_r_d. We repeat the same for the right numerator. Finally, we multiply the denominators of both fractions together—this will be the new denominator for both left and right fractions.

Step 3: Computing the resulting fraction

Since we now have the same denominator, the next step is to add the new numerators and assign the denominator to the final fraction. Here’s the code to do just that:

// Step 3: Compute resulting fraction
f_w = 0; // whole number 0
f_n = new_l_n + new_r_n; //addition of numerator
f_d = new_l_d; // can be either new_l_d or new_r_d

Step 4: Simplifying the resulting fraction (converting to reduced form)

Next, we need to reduce the fraction to its simplest form before converting it back to the mixed fraction form. To do that, we calculate the GCD for the numerator and denominator of the final obtained fraction. Here’s the helper function to calculate that:

//GCD Helper function
int GCD(int a, int b)
{
while (a % b != 0)
{
int r = a % b;
a = b;
b = r;
}
return b;
}

Note: We don't need to go into the details of how this function works as this is not the main focus of our lesson.

We’ll pass our numerator and denominator to this function, and it will return their GCD to us. It’s implemented in our code as follows:

Press + to interact
//Reduce the fraction (GCD) - Step 3
int gcd = GCD(f_n, f_d);
f_n /= gcd;
f_d /= gcd;
  • Line 2: We call our helper function and pass f_n (numerator) and f_d (denominator) as its parameters.

  • Lines 3–4: We reduce the fraction by dividing the numerator and denominator by their GCD.

Step 5: Convert to a mixed fraction

Now that we have our final reduced answer in the improper form, we need to convert it back to a mixed fraction. To do that, we use the mixed_fraction() helper function implemented below:

Press + to interact
// Simplify fraction
void mixed_fraction(int &f_w, int &f_n, int &f_d)
{
// Compute Sign
int sign_mf = compute_sign(f_w, f_n, f_d);
// Convert to Mixed Fraction
f_w = abs(f_n) / abs(f_d);
f_n = abs(f_n) % (f_d);
// Attach sign with the respective non-zero number
if (f_w)
f_w *= sign_mf;
else
f_n *= sign_mf;
}
  • Line 5: As mentioned earlier, the sign is associated with the whole fraction, not its components. So, we need to normalize each component and compute its overall sign. The first step is to calculate the sign for the fraction. So, we’ll again compute the sign of our resulting fraction by calling the compute_sign() function.

  • Lines 8–9: To convert the result back to a mixed fraction, we divide the dividend (numerator) by the divisor (denominator). The quotient becomes the new whole number, the remainder becomes the new numerator, and the denominator remains the same. Since / (division) of an integer in C++ removes the decimal part (remainder), we obtain the quotient (new whole number) and store it in f_w. Likewise, we use % (percent) to calculate the remainder and store it in f_n, our new numerator.

  • Lines 12–15: For the last step, we assign the sign to the final mixed fraction. We check if the whole number exists since there could be a case where the whole number becomes 0. If it exists, the sign is multiplied by the whole number; otherwise, the sign is multiplied by the fraction’s numerator.

Printing the resulting mixed fraction

In the last step, let’s print the mixed fraction in our main() function using the following command:

print ("L + R = ", f_w, f_n, f_d);

Note: We made a special print() helper function to print the fraction in mixed form. The details of that function are outside the scope of this course.

Complete code

Here’s what the complete code looks like:

Press + to interact
#include <iostream>
using namespace std;
//GCD helper function
int GCD(int a, int b)
{
while (a % b != 0)
{
int r = a % b;
a = b;
b = r;
}
return b;
}
//Improper fraction numerator calculator helper function
int improper_fraction_numerator(int w, int n, int d)
{
return abs(d) * abs(w) + abs(n);
}
//Compute sign for the final Mixed Fraction
int compute_sign(int w, int n, int d)
{
int sign_w, sign_n, sign_d;
if (w <0)
sign_w = -1;
else
sign_w = 1;
if (n <0)
sign_n = -1;
else
sign_n = 1;
if (d <0)
sign_d = -1;
else
sign_d = 1;
return sign_w * sign_n * sign_d;
}
//Print helper function
void print(string msg, int l_w, int l_n, int l_d)
{
cout<< msg <<" = " << l_w << "<" << l_n << "/" << l_d << ">\n";
}
//Convert Mixed fraction to Improper fraction
void improper_fraction(int w, int n, int d, int &new_n, int &new_d)
{
int temp_numerator = improper_fraction_numerator(w, n, d);
temp_numerator *= compute_sign(w, n, d);
int temp_denominator = d;
new_n = temp_numerator;
new_d = temp_denominator;
}
//Make the Improper Fractions' denominator same
void same_denominator(int &new_l_n, int &new_l_d, int &new_r_n, int &new_r_d)
{
// 1: left n = left n * right d
int temp_l_n = new_l_n * new_r_d;
// 2: left n = right n * left d
int temp_r_n = new_r_n * new_l_d;
// 3: Denominator calulation
int temp_d = new_l_d * new_r_d;
//Assigning values
new_l_n = temp_l_n, new_l_d = temp_d;
new_r_n = temp_r_n, new_r_d = temp_d;
}
//Convert back to Mixed fraction
void mixed_fraction(int &f_w, int &f_n, int &f_d)
{
// Simplify fraction
// Compute Sign
int sign_mf = compute_sign(f_w, f_n, f_d);
// Convert to Mixed Fraction
f_w = abs(f_n) / abs(f_d);
f_n = abs(f_n) % (f_d);
// Attach sign with the respective non-zero number
if (f_w)
f_w *= sign_mf;
else
f_n *= sign_mf;
}
//ADDITION OPERATION
void add(int l_w, int l_n, int l_d, int r_w, int r_n, int r_d,
int &f_w, int &f_n, int &f_d)
{
//Step 1: Convert to Improper Fraction //
int new_l_n, new_l_d;
int new_r_n, new_r_d;
improper_fraction(l_w, l_n, l_d, new_l_n, new_l_d);
improper_fraction(r_w, r_n, r_d, new_r_n, new_r_d);
//Addition//
//Step 2: Making d same
same_denominator(new_l_n, new_l_d, new_r_n, new_r_d);
//Step 3: Compute resulting fraction
f_w = 0;
f_n = new_l_n + new_r_n;
f_d = new_l_d;
//Step 4: Reduce the fraction (GCD)
int gcd = GCD(f_n, f_d);
f_n /= gcd;
f_d /= gcd;
//Step 5: Convert to Mixed Fraction & Print
mixed_fraction(f_w, f_n, f_d);
}
int main()
{
int f_w, f_n, f_d ; //final fraction
int l_w = -2, l_n = 1, l_d = 2; //left fraction
int r_w = 0, r_n = 1, r_d = 2; //right fraction
print ("Left",l_w, l_n, l_d);
print ("Right",r_w, r_n, r_d);
add(l_w, l_n, l_d,
r_w, r_n, r_d,
f_w, f_n, f_d);
print ("Left + Right", f_w, f_n, f_d);
return 0;
}

That’s it! We just wrote the code for a mixed fraction addition calculator. Play around with this code widget by trying different values and see how the addition calculator works.