I just started C and need some help.
Basically, my code is working the way I want. However, as you can see below, when I type a number and a letter, the code still counts the variable as only a number.
I want the same error message that displays when someone types a letter then a number the same way for this. I know it has something to do with my function, but when I tried doing isNaN(Not-a-Number)
the same problem occurred. What am I doing wrong?
I have included my code for the functions validation where the problem is:
#include "functions.h"
#include <iostream>
using namespace std;
double getNumber(){
double temperature = 0;
while (cout << "Please enter a temperature between -40 and 40 degrees Celsius: " && !(cin>>temperature))
{
cin.clear();
cin.ignore(1000, '\n');
cin.fail();
cout << "Bad value, try again..." << endl;
cout <<"\n";
}
return temperature;
}
double validRange(double min, double max){
double temperature = 0;
while(true){
temperature = getNumber();
if(temperature >= min && temperature <= max)
break;
else
{
cout << "Out of range, value must be between " << min << " and " << max << endl;
}
}
return temperature;
}
CodePudding user response:
User input is always a pain. You should:
- get a string from the user
- convert the entire string to what you want
- abort on fail (with error message)
I keep a little function around for just this purpose:
#include <optional>
#include <sstream>
#include <string>
template <typename T>
auto string_to( const std::string & s )
{
T value;
return (std::istringstream( s ) >> value >> std::ws).eof()
? value
: std::optional <T> {};
}
It can be used as:
#include <ciso646>
#include <iostream>
int main()
{
std::cout << "Please enter a temperature between -40 and 40: ";
std::string s;
getline( std::cin, s );
auto temperature = string_to <double> ( s );
if (!temperature or (*temperature < -40) or (*temperature > 40))
{
std::cerr << "That was not a valid temperature.\n";
return 1;
}
std::cout << "Good job!\nYou entered " << *temperature << "°.\n";
}
You can even wrap common code (like asking for a number in a certain range of values) up into a little function if you intend to use it a lot:
#include <ciso646>
#include <cstdlib>
#include <iostream>
double ask_for_number(
const std::string & prompt,
double minimum,
double maximum,
const std::string & error_message )
{
std::cout << prompt;
std::string s;
getline( std::cin, s );
auto number = string_to <double> ( s );
if (!number or (*number < minimum) or (*number > maximum))
{
std::cerr << error_message << "\n";
exit( 1 );
}
return *number;
}
int main()
{
double temperature = ask_for_number(
"Please enter a temperature between -40 and 40: ",
-40, 40,
"That was not a valid temperature." );
std::cout << "Good job!\nYou entered " << temperature << "°.\n";
}
It seems like a lot of typing, but the point is that all the typing goes in one spot and it can be reused over and over and over, and it works correctly, expecting the user to provide a single input for a single prompt and properly failing on invalid input.
CodePudding user response:
To check the validity of the input, I would suggest utilizing stod() and exception handling. Because cin
takes as many expressions that can be interpreted to number as possible, and returns nonzero value if that happened. However, stod()
can check the number of characters that was parsed, and if we check whether whole expression is parsed, and if not, throw
, we can perform the validity check. Here's the modified getnumber():
double getNumber() {
double temperature;
bool got_the_value = false;
while (!got_the_value) {
try {
cout << "Please enter a temperature between -40 and 40 degrees Celsius: ";
string tmpstring;
cin >> tmpstring;
size_t pos;
temperature = stod(tmpstring,&pos);
if (tmpstring.size() != pos) {
cerr << "Bad value, try again..." << endl;
continue;
}
got_the_value = true;
}
catch (exception& e) {
cerr << "Bad value, try again..." << endl;
}
}
return temperature;
}