Stop using namespace std;
- bringing everything defined in std
into the global namespace can lead to breakage (particularly if you later include more standard headers or move to a newer C++ version).
We're missing the necessary include (<string>
) to define std::string
.
We don't need to flush output anywhere here, so we can replace std::endl
with plain newline ('\n'
).
The prompt should tell the user how to stop.
Instead of prompting with i+1
, it's easier to start i
at 1, keeping its meaning clear.
When reading from std::cin
or any other stream outside the program's control, it's important not to use the results unless the read was successful. In this code, we never check whether input was read successfully, so the contents of course
, hours
and marks
cannot be used with any confidence.
The indentation is screwy here:
if (course=="stop")
break;
cout<<"Enter "<<course<<" Credit Hours: ";
cin>>hours;
Good tools exist to fix that automatically.
The big if
/else
chain is probably better moved out to a function, to reduce the duplication:
#include <stdexcept>
static double multiplier_for(double marks)
{
if (marks < 60 || marks > 100) {
throw std::domain_error("Marks should be in range 60-100");
}
if (marks < 65) { return 2.00; }
if (marks < 70) { return 2.50; }
if (marks < 75) { return 3.00; }
if (marks < 80) { return 3.50; }
if (marks < 85) { return 4.00; }
if (marks < 90) { return 4.50; }
if (marks < 95) { return 4.75; }
return 5.00;
}
Then we can use it within main()
:
double points = hours * multiplier_for(marks);
We'll have to catch the exception; we don't need goto
to repeat the prompt and input. We can use a loop which terminates when there's no exception:
for (;;) {
try {
std::cout << "Enter " << course << "'s marks from 60 to 100: ";
double marks;
std::cin >> marks;
double points = hours * multiplier_for(marks);
totalp += points;
totalh += hours;
break; // if no exception thrown
} catch (std::exception& e) {
std::cout << e.what() << '\n';
}
}
The final output should be a full line (ending in \n
):
std::cout << "Your GPA is: " << gpa << '\n'
Modified program
This includes a robust input-retry loop, which is surprisingly hard to do well. We might consider creating a function to retry each input separately, of course:
#include <iostream>
#include <limits>
#include <stdexcept>
#include <string>
static double multiplier_for(double marks)
{
if (marks < 60 || marks > 100) {
throw std::domain_error("Marks should be in range 60-100");
}
if (marks < 65) { return 2.00; }
if (marks < 70) { return 2.50; }
if (marks < 75) { return 3.00; }
if (marks < 80) { return 3.50; }
if (marks < 85) { return 4.00; }
if (marks < 90) { return 4.50; }
if (marks < 95) { return 4.75; }
return 5.00;
}
int main()
{
std::cout << "Welcome to GPA Calculator\n";
std::cin.exceptions(std::ios_base::badbit|std::ios_base::failbit);
double totalh = 0;
double totalp = 0;
for (unsigned int i = 1; !std::cin.eof(); ++i) {
for (;;) {
try {
std::cout << "Enter Course #" << i << " name ('stop' to finish): ";
std::string course;
std::cin >> course;
if (course == "stop") {
break;
}
std::cout << "Enter " << course << " Credit Hours: ";
double hours;
std::cin >> hours;
std::cout << "Enter " << course << "'s marks from 60 to 100: ";
double marks;
std::cin >> marks;
totalh += hours;
totalp += hours * multiplier_for(marks);
break; // if no exception thrown
} catch (std::exception& e) {
std::cout << e.what() << '\n';
if (std::cin.eof()) { break; }
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
}
double gpa = totalp / totalh;
std::cout << "Your GPA is: " << gpa << '\n';
}