I'm supposed to write a program that reads student IDs and grades from a file, with 3 functions:
getResults: this function reads from the file and counts how many failed (if score < 55) calculates the average and returns the number of students.
display: displays everything to the screen
isF: checks whether the score is less than 55 and returns true if it is. (In other words, if the student failed it will return true.)
My program is working fine up to the checking how many failed. I'm almost certain its a logical error but I can't figure out where. The number of failed students is not being calculated correctly. I keep getting 1 when it is supposed to be 2 as per the sample file.
#include <iostream>
#include <fstream>
using namespace std;
int getResults(ifstream &input, int &failed, double &average);
bool isF(int score);
void display(int num_students, double average, int num_failed);
int main() {
int numfailed, numOfStudents;
double avg;
ifstream in_stream("grades.txt");
numOfStudents = getResults(in_stream, numfailed, avg);
display(numOfStudents, avg, numfailed);
system("PAUSE");
return 0;
}
int getResults(ifstream &input, int &failed, double &average) {
int studentID, studentGrade, total = 0, numberOfStudents = 0;
while (input >> studentID >> studentGrade) {
total = total + studentGrade;
numberOfStudents = numberOfStudents + 1;
failed = isF(studentGrade);
}
average = total / numberOfStudents;
return numberOfStudents;
}
bool isF(int score) {
if (score < 55)
return true;
else
return false;
}
void display(int num_students, double average, int num_failed) {
cout << "Number of Students: " << num_students << endl;
cout << "Class Average: " << average << endl;
cout << "Number of students failed: " << num_failed << endl;
}
My sample file is as follows:
- 333 95
- 123 40
- 111 88
- 121 70
- 110 55
- 909 45
Sample output:
Number of students: 6
Class Average: 65
Number of students failed: 2
The output I'm getting is exactly the same except that I get 1 student failed.
First initialise your numfailed to 0 (thanks # Peter), then change this:
failed = isF(studentGrade);
To this:
failed += isF(studentGrade);
Or this:
failed += isF(studentGrade) ? 1 : 0;
There are two things you need to change. First initialize failed in your function to zero and then modify to add the count of failures. Here is the modified function:
int getResults(ifstream &input, int &failed, double &average)
{
int studentID, studentGrade, total = 0, numberOfStudents = 0;
failed = 0;
while (input >> studentID >> studentGrade)
{
total = total + studentGrade;
numberOfStudents = numberOfStudents + 1;
failed += isF(studentGrade); //this ensures that every time a student fails, it is added
}
average = total / numberOfStudents;
return numberOfStudents;
}
Related
I am trying to expand on previous code by implementing 2D-array's, however I keep getting issues with the console not outputting the correct values. The console is not taking in the right values when calculating the average and outputs 0 instead of the expected value. When running the code, the section where it would display the High and the Low scores would always display the first number that was typed in.
There are restrictions to work under.
Adjust the logic to drop the high score and the low score and average the remaining three scores for each student. The student grade is based on the average of the 3 middle scores.
All data is read in from the keyboard.
Two global constants may be utilized: one for the number of students and one for the number of tests.
Display in a table format the student's name, 5 test scores, average, and grade. Include a header in the table to label each column respectively.
Use iomanip and setw() to format the output.
Main should consist of variable declarations and function calls. This means the for loops to process the arrays resides in the functions, not in main.
Has to follow the base code.
`
using namespace std;
const int SCORES = 5;
const int NUM_STUDENTS = 3;
int main()
{
string name[NUM_STUDENTS];
int test[NUM_STUDENTS][SCORES];
char grade[NUM_STUDENTS];
float avg{};
int total = 0;
int hiIndex{}, loIndex{};
calcData(name, test, grade, total, hiIndex, loIndex, avg);
//display results
displayResults(name, test, grade, avg, loIndex, hiIndex);
system("pause");
return 0;
}
void calcData(string name[], int test[][SCORES], char grade[], int total, int hiIndex, int loIndex, float& avg)
{
for (int counter = 0; counter < NUM_STUDENTS; counter++)
{
getInput(name, test, counter, total);
cin.ignore();
//find index of the highest score and lowest score
findHiAndLow(test, hiIndex, loIndex, counter);
//assign letter grade
assignGrade(avg, grade, counter);
//calculate the class average
calcAvg(total - (test[counter][hiIndex] + test[counter][loIndex]), avg, SCORES - 2);
}
}
void getInput(string arrOne[], int arrTwo[][SCORES], int size, int& t)
{
//get student name
cout << "Input the student name and press enter\n";
getline(cin, arrOne[size]);
for (int i = 0; i < SCORES; i++)
{
//get student test score
cout << "Input the score for the midterm test\n";
cin >> arrTwo[size][i];
//(accumulate scores) total of all scores
t += arrTwo[size][i];
}
cout << endl;
}
int findHiAndLow(int t[][SCORES], int& h, int& l, int row)
{
for (int i = 0; i < SCORES; i++)
{
if (t[row][h] < t[row][i])
h = row;
if (t[row][l] > t[row][i])
l = row;
}
return h, l;
}
float calcAvg(int t, float a, int size)
{
a = static_cast<float>(t) / size;
return a;
}
void displayResults(string n[], int t[][SCORES], char g[], float a, int low, int high)
{
for (int counter = 0; counter < NUM_STUDENTS; counter++)
{
cout << left << setw(10) << n[counter] << ":";
for (int i = 0; i < SCORES; i++)
{
cout << setw(10) << t[counter][i];
}
cout << endl;
}
cout << "\n\nThe class average for this test = " << a << endl << endl;
for (int i = 0; i < NUM_STUDENTS; i++)
{
cout << n[i] << " your highest test score = " << t[i][high] << endl;
cout << n[i] << " your lowest test score = " << t[i][low] << endl << endl;
}
}
`
The expected outcome was for the program to take the average of the 3 middle scores that are left after dropping both the high and low scores from the initial 5 scores that are given. I have tried rearranging the values in both findHiandLow() and getInput(). I have tried having both for loops for getInput() within the function and have switched back to having one on the outside (within calcData()) to include the other functions, with the intent of having it loop for each student.
I wanted the console to print out the average of the three middle scores and not include the High and low, I was also expecting the console to print out the High and low scores for the student but it only prints the first score.
If my numbers were, for example, 12, 89, 45, 100, 23; The expectation would've been that it would drop the 12 and 100 and leave me with 89, 45, and 23. It would then take the average of those 3 numbers which in theory should result in 52.34 and result in an "F", however it prints out 0. and because the number that was first typed in was 12 the lowest and highest number would be listed as 12. It should have been 12 and 100 respectively.
This is another case of the incredibly common newbie confusion over returning values from functions.
This is your function
float calcAvg(int t, float a, int size)
{
a = static_cast<float>(t) / size;
return a;
}
You are trying to calculate the average and return the result, but for some reason you have declared a as a parameter, instead of as a local variable. This is how it should look
float calcAvg(int t, int size)
{
float a = static_cast<float>(t) / size;
return a;
}
Once you see that you should see that it can be further simplified, eliminating a altogether
float calcAvg(int t, int size)
{
return static_cast<float>(t) / size;
}
Now look at how you call calcAvg.
calcAvg(total - (test[counter][hiIndex] + test[counter][loIndex]),
avg, SCORES - 2);
you are calling the function, but doing nothing with the returned value. This is how it should look
avg = calcAvg(total - (test[counter][hiIndex] + test[counter][loIndex]),
SCORES - 2);
Now the return value of calcAvg is being assigned to the variable avg changing it's value. This is clearly what you intended. If you want to change the value of a variable using a functions return value the syntax is x = func(); not func(x).
Really not sure why this is such a stumbling block for newbies. The correct code has always seemed natural and straightforward to me. But, in any case, remember that parameters and return values are different things, with different syntax, and different purposes.
I'm having this strange issue where my code does exactly what I want it to when I run it through my debugger, but it doesn't work when I run it normally. What's stranger is that I am not getting a runtime error either. This is where the issue might be, but I'm not 100% sure on this:
void calcMostMissed (double *ptr, int totalContestants, int arrSize)
{
//output header
cout << endl << "MOST MISSED QUESTIONS" << endl;
//check how many times a question is missed and its percentage, then output it if it's above 60%
double curQuest = *ptr;
int freq = 0;
int j = 0;
double percentMissed;
for (int i = 0; i <= arrSize; i++) //loop through the missed questions array
{
if (*(ptr + i) > 0) //if pointer is pointing at a question number (extra space in array is set to 0)...
{
if (*(ptr + i) == curQuest) //then check how often it occurs in the array
freq++;
}
else //if the pointer is not pointing at a question number anymore...
{
//calculate percent missed and output it if its 60% or greater
percentMissed = (static_cast<double>(freq) / totalContestants) * 100;
if (percentMissed >= 60)
{
cout << static_cast<int>(curQuest) << "\t" << fixed << setprecision(2) << percentMissed << "%" << endl;
}
// check if the question's percentage missed has already been calculated and evaluated
j++;
curQuest = *(ptr + j);
int r = 0;
while (r < j)
{
if (*(ptr + r) == curQuest)
{
if (j < arrSize - 1)
{
j++;
curQuest = *(ptr + j);
}
}
r++;
}
if (!(j == arrSize - 1 && r == arrSize - 1))
{
i = 0;
}
freq = 0;
}
//if the current question variable has been through all missed question, then leave loop
if (curQuest < 1)
{
break;
}
}
}
What this function is supposed to do overall is find the percent missed on all missed questions and output only the ones that are above 60% inclusive.
This is what my debugger outputs (and what I want it to look like):
Enter name of answer key file: batch7a.txt
Enter name of contestant's answers file: allWrongContestants.txt
oo12345678 - 0.00
1 2 3
C A B
A B C
0012387654 - 0.00
1 2 3
C A B
A B C
0012364213 - 0.00
1 2 3
C A B
A B C
Mean: 0.00
Median: 0.00
Mode: 0.00
MOST MISSED QUESTIONS
1 100.00%
2 100.00%
3 100.00%
This is what a normal execution ouputs:
Enter name of answer key file: batch7a.txt
Enter name of contestant's answers file: allWrongContestants.txt
oo12345678 - 0.00
1 2 3
C A B
A B C
0012387654 - 0.00
1 2 3
C A B
A B C
0012364213 - 0.00
1 2 3
C A B
A B C
Mean: 0.00
Median: 0.00
Mode: 0.00
MOST MISSED QUESTIONS
Process returned 0 (0x0) execution time : 14.442 s
Press any key to continue.
My debugger runs through all the way to the end of my program just fine, but for some reason the normal execution is still flawed. Any suggestions are welcome and I'll clarify anything that I may have forgotten to mention.
If it helps, I am using Code::Blocks as my IDE
Some other info about my code: there are 3 questions and all contestants scored a 0% on the test, meaning all questions have the incorrect answers.
Output: The numbers are the contestant's ID, next to that is their score, the list of numbers underneath is the question number they got wrong, underneath that are the contestant's answers, and under that are the correct answers.
This is the function that calls the calcMostMissed function:
void statReport (double *ptr, int totalScores, double *mmqPtr, int arrSize)
{
//calculate mean of scores
double mean = calcMean(ptr, totalScores);
//calculate median of scores
double median = calcMedian(ptr, totalScores);
//calculate mode of scores
double *mode = calcMode(ptr, totalScores);
//output to console the data
cout << "Mean: " << fixed << setprecision(2) << mean << endl;
cout << "Median: " << median << endl;
cout << "Mode: ";
sort(mode, mode + totalScores);
int j = 0;
for (int i = 0; i < totalScores; i++)
{
if (*(mode + i) != -1)
{
if (j == 0)
{
cout << *(mode + i);
}
else
{
cout << ", " << *(mode + i);
}
j++;
}
}
cout << endl;
//call calcMissedQuestions function
calcMostMissed(mmqPtr, totalScores, arrSize);
//delete pointers
delete[] mode;
mode = nullptr;
delete[] mmqPtr;
mmqPtr = nullptr;
}
And this is the main function:
int main()
{
string answerKeyName;
string contestantAnswerName;
ifstream answerKeyFile;
ifstream contestantAnswerFile;
//ask for file names
cout << "Enter name of answer key file: ";
cin >> answerKeyName;
cout << "Enter name of contestant's answers file: ";
cin >> contestantAnswerName;
//check if files can be opened properly
answerKeyFile.open(answerKeyName, ios::binary);
contestantAnswerFile.open(contestantAnswerName, ios::binary);
if(!answerKeyFile)
{
cout << "Answer key file could not be opened" << endl;
exit(EXIT_FAILURE);
}
if (!contestantAnswerFile)
{
cout << "Contestant's answers file could not be opened" << endl;
exit(EXIT_FAILURE);
}
//find how many contestant's there are
int i = 0;
string temp;
while (contestantAnswerFile.good())
{
getline(contestantAnswerFile, temp);
if (temp != "")
{
i++;
}
}
contestantAnswerFile.clear();
contestantAnswerFile.seekg(0, ios::beg);
//create contestant's score array
double *scorePtr = new double[i];
//create pointer for all missed questions
double *mmqPtr = nullptr;
int arrSize;
//compare the contestant's answers to the answer key, then fill array with score
double score;
for (int c = 0; c < i; c++)
{
score = compareAnswers (contestantAnswerFile, answerKeyFile, mmqPtr, i, arrSize);
*(scorePtr + c) = score;
}
//create the statistics report
statReport(scorePtr, i, mmqPtr, arrSize);
//delete dynamically allocated array
delete[] scorePtr;
scorePtr = nullptr;
//close files
answerKeyFile.close();
contestantAnswerFile.close();
return 0;
}
Below is where I think the issue resides, however I still cannot properly compile it because other functions and input files are not provided, probably rightly so in order not to further congest the question:
double *mmqPtr = nullptr; is defined in main(), then it's passed to the statReport() function without having been associated with any object.
Afterward it acts as first parameter for function calcMostMissed() and gets dereferenced inside it, but is still a nullptr, so this produces undefined behaviour.
There is also the issue of an out-of-bounds index inside a loop, as pointed at in the comments.
I want to read the data from my input file
70 95 62 88 90 85 75 79 50 80 82 88 81 93 75 78 62 55 89 94 73 82
and store each value in an array. There's more to this particular problem (the other functions are commented out for now) but this is what's really giving me trouble. I looked for hours at the previous question about data and arrays but I can't find where I'm making the mistake.
Here's my code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const int SIZE = 22;
int grades[SIZE];
void readData() {
int i = 0;
int grades[i];
string inFileName = "grades.txt";
ifstream inFile;
inFile.open(inFileName.c_str());
if (inFile.is_open())
{
for (i = 0; i < SIZE; i++)
{
inFile >> grades[i];
cout << grades[i] << " ";
}
inFile.close(); // CLose input file
}
else { //Error message
cerr << "Can't find input file " << inFileName << endl;
}
}
/*
double getAverage() {
return 0;
}
void printGradesTable() {
}
void printGradesInRow() {
}
void min () {
int pos = 0;
int minimum = grades[pos];
cout << "Minimum " << minimum << " at position " << pos << endl;
}
void max () {
int pos = 0;
int maximum = grades[pos];
cout << "Maximum " << maximum << " at position " << pos << endl;
}
void sort() {
}
*/
int main ()
{
readData();
return 0;
}
and here's my output:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Thank you for your time.
The issue is that you're declaring a local grades array with a size of 1, hiding the global grades array. Not only that, you are now accessing the array beyond the bounds, since the local grades array can only hold 1 item.
So get rid of the line:
int grades[i];
However, it needs to be mentioned that this:
int i = 0;
int grades[i];
is not valid C++ syntax. You just stumbled into this by mistake, but that code would not compile if compiled using a strict ANSI C++ compiler.
An array in C++ must be declared using a constant expression to denote the number of entries in the array, not a variable. You, by accident, are using a non-standard compiler extension called Variable Length Arrays or VLA's for short.
If this is for a school assignment, do not declare arrays this way (even if you meant to do it), as it is not officially C++. If you want to declare a dynamic array, that is what std::vector is for.
I don't see any issue in reading the file , you have just confused the global vs local variable of grades
You don't need this
int i = 0;
int grades[];
Inside the function readData
#include <string>
using namespace std;
const int SIZE = 22;
int grades[SIZE];
void readData() {
string inFileName = "grades.txt";
ifstream inFile;
inFile.open(inFileName.c_str());
if (inFile.is_open())
{
for (int i = 0; i < SIZE; i++)
{
inFile >> grades[i];
cout << grades[i] << " ";
}
inFile.close(); // CLose input file
}
else { //Error message
cerr << "Can't find input file " << inFileName << endl;
}
}
int main()
{
readData();
return 0;
}
Your original global array grades, of size 22, is replaced by the local array with the same name but of size 0.
(It is not overwritten, just any code using the variable grades, inside the scope that the second one was defined, will read the value of the second grades array, as it has a higher precedence.)
Both inFile >> grades[i]; and cout << grades[i] << " "; should return runtime errors as you are reading beyond their size (It appears that you are not using a strict compiler).
[int grades[i]; would return a compile time error normally as you shouldn't / usually can't initialize a fixed array with a variable]
I think what is happening, instead of your program crashing, is that grades[i] is just returning an anonymous instance of a variable with value 0, hence your output.
The simplest fix to your problem would be to just delete int grades[i].
(Also delete one of the int i = 0's as you don't need that to be defined twice)
Currently, I am taking a C++ course at my local college, and was given a debugging assignment. In the instructions for this assignment, I was told that the only thing really wrong with this code, is that the conditions for the nested if-else-if statements on lines 82-89 are redundant, however, I cannot see another way to get the same results without having those conditions stay the same...any tips or such would be greatly appreciated!
#include <iostream>
#include <conio.h>
#include <iomanip>
using namespace std;
const int BASE_COST = 10;
const int LOW_LIMIT = 20;
const int MID_LIMIT = 40;
const int HIGH_LIMIT = 60;
const double LOW_CHECKS = .10;
const double MIDLOW_CHECKS = .08;
const double MIDHIGH_CHECKS = .06;
const double HIGH_CHECKS = .04;
int main()
{
int numOfChecks;
double multiplierValue;
double totalFee;
cout << fixed << showpoint;
cout << setprecision(2);
cout << "Please enter the number of checks you used this month: ";
cin >> numOfChecks;
if(numOfChecks < 0)
{
cout << "Number of checks can't be negative. Program ends.\n";
exit(1); //terminate the program with error code 1
}
//the following line runs only if the program did not terminate, so start over if-else
if(numOfChecks < LOW_LIMIT)
multiplierValue = LOW_CHECKS;
else if(numOfChecks < MID_LIMIT)
multiplierValue = MIDLOW_CHECKS;
else if(numOfChecks >= MID_LIMIT && numOfChecks < HIGH_LIMIT)
multiplierValue = MIDHIGH_CHECKS;
else if (numOfChecks >= HIGH_LIMIT)
multiplierValue = HIGH_CHECKS;
totalFee = BASE_COST + numOfChecks * multiplierValue;
cout << "Your total for this month is $" << totalFee;
_getch();
return 0;
}
This part else if(numOfChecks >= MID_LIMIT && numOfChecks < HIGH_LIMIT) looks redundant. Provided you keep the order of the range checks, it can be simplified to just else if (numOfChecks < HIGH_LIMIT), same as the one following it (which is just not needed) so that the whole piece looks like:
//the following line runs only if the program did not terminate, so start over if-else
if (numOfChecks < LOW_LIMIT)
multiplierValue = LOW_CHECKS;
else if (numOfChecks < MID_LIMIT)
multiplierValue = MIDLOW_CHECKS;
else if (numOfChecks < HIGH_LIMIT)
multiplierValue = MIDHIGH_CHECKS;
else
multiplierValue = HIGH_CHECKS;
Indeed, all the conditions are redundant: use an algorithm :)
Live On Coliru
#include <iomanip>
#include <map>
#include <iostream>
namespace {
using Threshold = unsigned;
using Rate = double;
static Rate constexpr BASE_COST = 10.0;
std::map<Threshold, Rate, std::greater<> > const tariffs {
{ 0, .10},
{20, .08},
{40, .06},
{60, .04},
};
double fee(unsigned numOfChecks) {
auto rate = tariffs.lower_bound(numOfChecks);
return BASE_COST + rate->second * numOfChecks;
}
}
int main() {
unsigned numOfChecks;
std::cout << "Please enter the number of checks you used this month: ";
if (std::cin >> numOfChecks) {
std::cout
<< "\nYour total for this month is $"
<< std::fixed << std::showpoint << std::setprecision(2) << fee(numOfChecks)
<< std::endl;
} else {
std::cout << "Invalid input\n";
return 1;
}
}
Prints e.g.
Please enter the number of checks you used this month: 10
Your total for this month is $11.00
Please enter the number of checks you used this month: 20
Your total for this month is $11.60
Please enter the number of checks you used this month: 30
Your total for this month is $12.40
Please enter the number of checks you used this month: 40
Your total for this month is $12.40
Please enter the number of checks you used this month: 50
Your total for this month is $13.00
Please enter the number of checks you used this month: 60
Your total for this month is $12.40
Please enter the number of checks you used this month: 70
Your total for this month is $12.80
Please enter the number of checks you used this month: 80
Your total for this month is $13.20
Please enter the number of checks you used this month: 90
Your total for this month is $13.60
Please enter the number of checks you used this month: 100
Your total for this month is $14.00
Im trying to calculate the average out of a couple of number inside an char array. The reason for this is that i imported data from a text document and i made it read only every 2nd line to get the numbers that i wanted.
Now i need to get the average out of these numbers but i cant make it work. I'm starting to get mad about this and i feel that the solution would be rather simple.
EDIT:
The file consists of names and numbers. I.e:
Jason Smith
32
Mary Jane
52
Stevie Wonder
68
Micheal Jackson
59
#include <fstream>
#include <iostream>
using namespace std;
double averageNum(char array[], int size) { // A function to calculate the average number
int sum = 0;
double avg;
for(int i=0; i<size; i++){
array[i]+= sum;
}
avg = sum / size;
return avg;
}
int main(){
char age [50][30];
double avg;
int rows = 0;
ifstream elevInfo("elevinfo.txt"); // Opens a stream to get data from the document
if (! elevInfo){ // Error message if the file couldn't be found
cout << "Could not find the file elevinfo.txt" << endl;
return (1);
}
while(elevInfo.getline(age[rows/2], 30)){ // Reading every 2nd line to an array
rows++;
}
avg = averageNum(age[], rows); // Function call with the numbers from the array and the variable rows as a pointer
cout << "Average age equals: " << avg << endl;
}
this is one of many possible solutions for your problem:
int main()
{
// Opens a stream to get data from the document
ifstream elevInfo("elevinfo.txt");
// Error message if the file couldn't be found
if (!elevInfo)
{
cout << "Could not find the file elevinfo.txt" << endl;
return (1);
}
int sum = 0;
int lineCounter = 0;
// loop till end of file
while (!elevInfo.eof())
{
// prepare buffer
char line[30];
// read line into buffer
elevInfo.getline(line, 30);
// do this every second line
if (lineCounter % 2 == 1)
{
// get age as int using function atoi()
int age = atoi(line);
// increase sum by the current age
sum += age;
}
// increment line-counter
lineCounter++;
}
// calculate the average
// divide lineCounter by 2 because only every second line in your file contains an age
double avg = sum / (lineCounter / 2.0);
cout << "Average age equals: " << avg << endl;
return 0;
}
As you can see it also doesn't need the function averageNum.