Statistical Measures Application: Grade Calculator

Grade calculator (application program)

Given the students’ information, including their roll numbers, names, and achieved marks, the program does the following:

  1. It reads the students’ data from a file called Semester.txt.
  2. It assigns the grade to each student based on the marks achieved (using the bell curve strategy).
  3. It counts the total number of each grade received.
  4. It compiles and saves the result in a new file called Results.txt.

What we see in the image above is a bell curve used for grading when we want to assign the grades over a normal distribution. Bell curving is usually used when we want to assign grades close to some specific range (the mean). This way, it conforms more closely to a bell-shaped curve, where most scores cluster around the average and fewer scores are at the extremes.

For this, we first calculate the mean and then assign the grade C to a range around the mean. Then, as seen in the image above, we add half the standard deviation to the mean (0.5 * std deviation + mean) to get the grade B. To assign the grade A, we add one and a half of the standard deviation to the mean (1.5 * std deviation + mean).

Similarly, we subtract half the standard deviation from the mean (-0.5 * std deviation + mean) to get the grade D. For the grade F, we subtract one and a half of the standard deviation from the mean (-1.5 * std deviation + mean).

This sums up the bell-curving method.

Implementation walkthrough

We will discuss all the steps one by one.

Reading the students’ data

Let’s start our program by loading the data using the function loadData().

void loadData(const char fname[], string RNs[], string Names[], double Marks[], int &total_records);

Parameters

  • const char fname[]: This is the file name where the data exists.
  • string RNs[]: This stores roll numbers read from the file.
  • string Names[]: This shows the stored names of the students read from the file.
  • double Marks[]: This stores the obtained marks of the students read from the file.
  • int &total_records: This specifies the number of records read from the file.

Implementation Details

  • First, the loadData() function will open the file specified by fname for reading.
  • Then, the first set of characters are read from the opened file that specify the number of records present in the file; therefore, this value is stored inside the total_records variable.
  • Then we loop the total_records number of times to read that many lines from the file and process them accordingly.

Let’s first grasp how the data is formatted inside the file to understand how we process the records. Each record corresponds to a single line where we have the following:

  • The first nine characters specify the roll number. Alternatively, we could also say that until a non-whitespace character is encountered, all the preceding characters constitute a roll number.
  • Then we get several characters (with a mixture of whitespace characters) that denote a student’s name until the . character is encountered.
  • Lastly, we have the obtained marks of the respective student.

Hence, to process this, we’ll do the following inside the loop:

  • Use the >> operator to read and store the contents from the file into the RNs array. Since there are now whitespace characters in the roll number, this will work.
  • Then, we’ll use the ignore() function on the reader to ignore the tab character (\t).
  • Next, we’ll use the getline() function to read the characters (including whitespace characters) until the . character is encountered and store them inside the Name array.
  • Lastly, we’ll again use the >> operator to read and store the contents from the file into the Marks array.

The loadData() function would then be as follows:

Press + to interact
void loadData(const char fname[], string RNs[], string Names[], double Marks[], int &total_records)
{
ifstream Rdr(fname);
Rdr>>total_records;
for(int i=0; i<total_records; i++)
{
Rdr>>RNs[i];
Rdr.ignore(); // discards the residual newline
getline(Rdr, Names[i], '.');
Rdr>>Marks[i];
}
}

Assigning grades

To the compute grades we’ll declare the grades() function with the following signature:

char grade(double amarks, double mu, double sd);

Parameters

  • double amarks: This specifies the obtained marks of a student.
  • double mu: This specifies the mean of the class.
  • double sd: This specifies the standard deviation of the class.

Implementation Details

  • The function will simply contain if conditions that reflect the strategy discussed above for assigning grades to students by utilizing the passed marks, mean, and standard deviation.
  • When a condition is met, the appropriate grade is returned to the callee.

The above method is invoked inside another function, computeEveryLetterGrade(), for each student. This function is responsible for computing the mean and standard deviation for the whole class by utilizing the auxiliary functions discussed in the previous lesson, mean(), variance(), and standardDeviation(). The following is the function’s signature:

void computeEveryLetterGrade(double Marks[], char Gs[], int gradeCounts[], int total_records);

Parameters

  • double Marks[]: This contains the obtained marks of the students.
  • char Gs[]: This contains the obtained grades of the students.
  • int gradeCounts[]: This contains the count of different student grades.
  • int total_records: This specifies the total number of records to process.

Implementation Details

  • Firstly, we compute the mean and standard deviation by utilizing the mean() and standardDeviation() functions.
  • Then, we loop total_records number of times and, in each iteration, do the following:
    • We use the grade() function to compute the grade assigned to the ith student and store it inside the Gs array.
    • Next, based on the grade obtained by the current student, we increment the corresponding entry of the gradeCounts array.
      • We accomplish this by checking whether the grade is F, and only proceed with the following strategy if it isn’t; otherwise, we increment the index 4 of the array.
      • To index the correct grade count to increment, we pass Gs[ri]-'A' as the index. Since both Gs[ri] and 'A' represent characters that, in a subtraction context, are cast to int, the appropriate index is yielded. The table below provides a dry run for different grades to clarify this:
Gs[ri] - char Gs[ri] - int 'A' - int Gs[ri]-'A' - int
'A' 65 65 65-65 = 0
'B' 66 65 66-65 = 1
'C' 67 65 67-65 = 2
'D' 68 65 68-65 = 3

Since 'F'-'A' would result in 70-65 = 5, whereas the index we need is 4, we cater the grade F case separately.

Press + to interact
char grade(double amarks, double mu, double sd)
{
if(amarks>=sd*1.5+mu)
{
return 'A';
}
else if(amarks>=sd*0.5+mu)
{
return 'B';
}
else if(amarks>=sd*(-0.5)+mu)
{
return 'C';
}
else if(amarks>=sd*(-1.5)+mu)
{
return 'D';
}
else
{
return 'F';
}
}
void computeEveryLetterGrade(double Marks[], char Gs[], int gradeCounts[], int total_records)
{
double mu = mean(Marks, total_records);
double sd = standardDeviation(Marks, total_records);
for(int ri=0; ri<total_records; ri++)
{
Gs[ri] = grade(Marks[ri], mu, sd);
if(Gs[ri]!='F')
gradeCounts[Gs[ri]-'A']++;
else
gradeCounts[4]++; // F Grade, this is only because we do not have any E grade
}
}

Printing the result

To print the result, we declare the printCompiledResult() function with the following signature:

void printCompiledResult(string RNs[], string Names[], double Marks[], char Gs[], int gradeCounts[], int total_records);

Parameters

  • string RNs[]: This contains the roll numbers of the students.
  • string Names[]: This contains the students’ names.
  • double Marks[]: This contains the obtained marks of the students.
  • char Gs[]: This contains the computed grades of the students.
  • int gradeCounts[]: This contains the overall grade statistics.
  • int total_records: This contains the total number of records to display.

Implementation Details

  • To print all the records, we simply iterate total_records number of times, and in each iteration print the ith content of the RNs, Names, Marks, and Gs arrays in a formatted manner.
  • Lastly, we also print the gradeCounts array independently, coupled with the grade it represents.
Press + to interact
void printCompiledResult(string RNs[], string Names[], double Marks[], char Gs[], int gradeCounts[], int total_records)
{
for(int i=0; i<total_records; i++)
{
cout<<i+1<<".\t"<<RNs[i]<<" "<<left<<setw(23)<<Names[i]<<"\t"<<Marks[i]<<"\t"<< Gs[i] <<endl;
}
cout << "________________________________________"<<endl;
cout << gradeCounts[0]<<" \tAs "<<endl;
cout << gradeCounts[1]<<" \tBs "<<endl;
cout << gradeCounts[2]<<" \tCs "<<endl;
cout << gradeCounts[3]<<" \tDs "<<endl;
cout << gradeCounts[4]<<" \tFs "<<endl;
}

Saving the result

To save the result, we declare the saveCompiledResult() function with the following signature:

saveCompiledResult(const char fname[], string RNs[], string Names[], double Marks[], char Gs[], int gradeCounts[], int total_records)

Parameters

  • const char fname[]: This contains the name of the file to save the result in.
  • string RNs[]: This contains the roll numbers of the students.
  • string Names[]: This contains the students’ names.
  • double Marks[]: This contains the obtained marks of the students.
  • char Gs[]: This contains the computed grades of the students.
  • int gradeCounts[]: This contains the overall grade statistics.
  • int total_records: This contains the total number of records to save.

Implementation Details

  • We first open a file writer for the file name specified in fname.
  • Then, similar to how we displayed data in the printCompiledResult() function, we use the same approach to save data to the file. However, to accomplish this, we use the file writer object as opposed to cout.
Press + to interact
void saveCompiledResult(const char fname[], string RNs[], string Names[], double Marks[], char Gs[], int gradeCounts[], int total_records)
{
ofstream Wrt(fname);
for(int i=0; i<total_records; i++)
{
Wrt<<i+1<<".\t"<<RNs[i]<<" "<<left<<setw(23)<<Names[i]<<"\t"<<Marks[i]<<"\t"<< Gs[i] <<endl;
}
Wrt << "________________________________________"<<endl;
Wrt << gradeCounts[0]<<" As "<<endl;
Wrt << gradeCounts[1]<<" Bs "<<endl;
Wrt << gradeCounts[2]<<" Cs "<<endl;
Wrt << gradeCounts[3]<<" Ds "<<endl;
Wrt << gradeCounts[5]<<" Fs "<<endl;
}

Complete implementation

We have provided the complete implementation below for you to run and test. Note that the last line of the main() function contains a system() function call. This is done to print the contents written in the Results.txt file.

38

BSCS17067	Noor Awais.	100
BSCS19012	Muhammad Saad.	80.97
BSCS19025	Amna Akbar.	73.88
BSCS19082	Zain-ul-Abidin.	79.42
BSCS20001	Samee Haider.	95.88
BSCS20002	Umer Khalid.	92.36
BSCS20004	Sarah John.	90.94
BSCS20007	Muhammad Abdul Rehman.	85.17
BSCS20009	Nadeem.	81.62
BSCS20010	Muhammad Nawaz.	96.91
BSCS20013	Uzair Aamir.	85.41
BSCS20014	Muhammad Daniyal.	85.50
BSCS20015	Muhammad Abdullah.	88.47
BSCS20019	Hajirah Awan.	83.30
BSCS20020	Rameesha Fayyaz.	79.22
BSCS20031	Taqi Raza.	88.28
BSCS20032	Sheharyar Kemal.	84.12
BSCS20035	Faizan Aziz.	96.93
BSCS20036	Farrukh Hussain.	92.31
BSCS20037	Abdul Sammad.	61.88
BSCS20041	Muhammad Hasan Masroor.	84.25
BSCS20046	Faizan Imam.	84.33
BSCS20047	Noor Ali.	88.15
BSCS20049	Syeda Nayab Fatima.	74.42
BSCS20054	Ans Nazir.	84.43
BSCS20056	Muhammad Sabooh Taha.	98.31
BSCS20057	AizazUl Haq.	92.22
BSCS20060	Atashia.	82.45
BSCS20063	Fareeha.	84.43
BSCS20064	Usama.	83.47
BSCS20065	Osman Naveed.	86.3
BSCS20066	Ahsan Anwar.	69.05
BSCS20070	Uzair Amin.	90.12
BSCS20072	Ateeq Idrees.	86.60
BSCS20075	Hoorain Zeesahn.	77.10
BSCS20076	Jennifer.	86.73
BSCS20078	Abdullah Mushtaq.	96.68
BSCS20080	Muhammad Areeb Qamar.	80.72

Grade calculator

In line 85, gradeCounts[Gs[ri]-'A']++; is incrementing an element of the gradeCounts[] array based on the value of the current letter grade. The expression Gs[ri] - 'A' returns the numeric value of the current letter grade.

In the ASCII table, the letter 'A' has the value of 65 and 'F' has the value of 70. So, when a grade Gs[ri] is 'A', it has a value of 65, 'B' has a value of 66, 'C' has a value of 67 and so on till 'F' has a value of 70.

So, when a grade Gs[ri] is 'A', Gs[ri] - 'A' evaluates to 0, 'B' evaluates to 1, 'C' evaluates to 2, and so on. This allows the code to use the value of the current letter grade to index the corresponding element in the gradeCounts[] array and increment it.