I'm trying to write a script where the user will be inputting a radius and then the console will display the Volume and Surface Area of a sphere. If the input radius is negative, the user will be prompted to enter a positive radius until the condition is met. I've managed to do this but without validating the positive radius bit. How can I achieve this?
My code:
/*
* Calculate the volume and surface area of a sphere.
*
*/
#include <iostream>
#include <string>
#include <sstream>
#include <cmath> // Include cmath for M_PI constant
using namespace std;
int main()
{
const double pi = M_PI; /// Value of PI defined by C
string input = ""; /// Temporary input buffer
double r = 0.0; /// Sphere radius
double A = 0.0; /// Sphere area
double V = 0.0; /// Sphere volume
// Request radius
cout << "Please enter radius of sphere (positive only): ";
// Get string input from user (up to next press of <enter> key)
getline(cin, input);
// Try to convert input to a double
r = stod(input);
// making sure r is positive
if (r > 0)
{
// Calculate area and volume
// Ensure floating-point division instead of integer division by
// explicitly writing 4.0/3.0
A = 4.0 * pi * r * r;
V = (4.0 / 3.0) * pi * r * r * r;
// Write out result
cout << "Sphere radius: " << r << endl;
cout << "Sphere area: " << A << endl;
cout << "Sphere volume: " << V << endl;
}
else
{
while (r < 0)
{
cout << "Please enter radius of sphere (positive only): " << endl;
}
}
// Return success
return 0;
}
CodePudding user response:
First, this code is not awful. Compared to what I've seen from some other beginners, this code demonstrates that there is a decent understanding of fundamentals up to this point.
The biggest issue facing your code is the order of operations. If you want input from the user, you need to validate it before processing it. Currently, you're doing a bit of both at the same time. As mentioned, create a loop that does not exit until you have valid inputs. Then go ahead and do your math. This is separating your concerns and is a best practice.
Other nitpicks include using namespace std;
as a bad practice, and one you should get out of doing sooner than later. Front-declaring your variables is also bad practice. Declare at or near first use. std::string input;
suffices for a default string, there is no need to = "";
.
And as I commented, stod()
can throw an exception and abort your program if the input cannot be converted. You don't mention whether you're allowed to assume your input will always be a number or not so I can't assume it is.
/*
* Calculate the volume and surface area of a sphere.
*
*/
#include <cmath>
#include <iostream>
#include <numbers>
#include <string>
int main() {
double radius = 0.0;
bool inputIsInvalid = true;
do {
std::string input;
std::cout << "Enter a radius: ";
std::getline(std::cin, input);
std::size_t pos = 0;
try {
radius = std::stod(input, &pos);
} catch (const std::exception& e) {
std::cerr << "Unable to convert to double. Reason: " << e.what() << '\n';
continue;
}
// We're still not done checking. We need to ensure that the entire string
// was converted. If not, the input was invalid.
if (pos != input.length()) {
std::cerr << "Invalid characters added. Try again.\n";
continue;
}
// Making it here means a valid double was typed in.
// Now we ensure that the double is positive.
if (radius < 0.0) {
std::cerr << "Please enter a positive number. Try again.\n";
continue;
}
// Making it here should mean that we have a valid input.
inputIsInvalid = false;
} while (inputIsInvalid);
// Now we can do math!
using namespace std::numbers; // C 20 stuff for pi
double surfaceArea = 4.0 * pi * std::pow(radius, 2);
double volume = (4.0 / 3.0) * pi * std::pow(radius, 3);
std::cout << "For a sphere of radius: " << radius << '\n'
<< "Surface area: " << surfaceArea << '\n'
<< "Volume: " << volume << '\n';
}
Output:
❯ ./a.out
Enter a radius: foo
Unable to convert to double. Reason: stod: no conversion
Enter a radius: 3o
Invalid characters added. Try again.
Enter a radius: -3
Please enter a positive number. Try again.
Enter a radius: 3
For a sphere of radius: 3
Surface area: 113.097
Volume: 113.097
As you can see, all of the getting of input and validation occurs within the big do/while loop. If we are out of the loop, we know that we have a valid value, and doing the math is now very straightforward.
CodePudding user response:
You could use std::from_chars
within an infinite loop to read a radius of type double.
for (;;) {
std::cout << "Please enter radius of sphere (positive only): ";
// Get string input from user (up to next press of <enter> key)
std::string input = ""; /// Temporary input buffer
getline(std::cin, input);
// Try to convert input to a double
auto [ptr, ec] = std::from_chars(input.data(), input.data() input.size(), r);
if (ec != std::errc{} or ptr != input.data() input.size() or r < 0) {
std::cout << "Invalid input: '" << input << "'\n";
} else {
break;
}
}