Reputation: 979
So I am currently learning C++ and decided to make a program that tests my skills I have learned so far. Now in my code I want to check if the value that the user enters is a double, if it is not a double I will put a if loop and ask them to reenter it. The problem I have is how do I go about checking what type of variable the user enters, ex- if a user enters a char or string, I can output an error message. Here is my code:
//cubes a user entered number
#include <iostream>
using namespace std;
double cube(double n); //function prototype
int main()
{
cout << "Enter the number you want to cube: "; //ask user to input number
double user;
cin >> user; //user entering the number
cout << "The cube of " << user << " is " << cube(user) << "." << endl; //displaying the cubed number
return 0;
}
double cube (double n) //function that cubes the number
{
return n*n*n; // cubing the number and returning it
}
Edit: I would have to say I just started and don't have the slightest of clue about your code, but I will check out your link. By the way, I haven't learned how to work with templates yet,I am learning about dealing with data, only Chapter 3 in my C++ Primer Plus 5th edition.
Upvotes: 6
Views: 26066
Reputation: 507343
You can define a function for this using std::istringstream
:
#include <sstream>
bool is_double(std::string const& str) {
std::istringstream ss(str);
// always keep the scope of variables as close as possible. we see
// 'd' only within the following block.
{
double d;
ss >> d;
}
/* eat up trailing whitespace if there was a double read, and ensure
* there is no character left. the eof bit is set in the case that
* `std::ws` tried to read beyond the stream. */
return (ss && (ss >> std::ws).eof());
}
To assist you in figuring out what it does (some points are simplified):
operator>>
. This means skipping whitespace and trying to read a double.abc
the stream sets the fail-bit. Note that cases like 3abc
will succeed and will not set the fail-bit.ss
evaluates to a zero value, which means false. eof()
will return true if we tried to read past the end. std::ws
does exactly that), eof
will return true. Note this check makes sure that 3abc
will not pass our check.&&
evaluate to true, we return true to the caller, signaling the given string is a double.Similar, you check for int
and other types. If you know how to work with templates, you know how to generalize this for other types as well. Incidentally, this is exactly what boost::lexical_cast
provides to you. Check it out: http://www.boost.org/doc/libs/1_37_0/libs/conversion/lexical_cast.htm.
This way has advantages (being fast) but also major disadvantages (can't generalized using a template, need to work with raw pointers):
#include <cstdlib>
#include <cctype>
bool is_double(std::string const& s) {
char * endptr;
std::strtod(s.c_str(), &endptr);
if(endptr != s.c_str()) // skip trailing whitespace
while(std::isspace(*endptr)) endptr++;
return (endptr != s.c_str() && *endptr == '\0');
}
strtod
will set endptr
to the last character processed. Which is in our case the terminating null character. If no conversion was performed, endptr is set to the value of the string given to strtod
.
One might thing that std::sscanf
does the trick. But it's easy to oversee something. Here is the correct way to do it:
#include <cstdio>
bool is_double(std::string const& s) {
int n;
double d;
return (std::sscanf(s.c_str(), "%lf %n", &d, &n) >= 1 &&
n == static_cast<int>(s.size()));
}
std::sscanf
will return the items converted. Although the Standard specifies that %n
is not included in that count, several sources contradict each other. It's the best to compare >=
to get it right (see the manpage of sscanf
). n
will be set to the amount of the processed characters. It is compared to the size of the string. The space between the two format specifiers accounts for optional trailing whitespace.
If you are a beginner, read into std::stringstream
and do it the C++ way. Best not mess with pointers until you feel good with the general concept of C++.
Upvotes: 12
Reputation: 47085
There is no suitable way to check if a string really contains a double within the standard library. You probably want to use Boost. The following solution is inspired by recipe 3.3 in C++ Cookbook:
#include <iostream>
#include <boost/lexical_cast.hpp>
using namespace std;
using namespace boost;
double cube(double n);
int main()
{
while(true)
{
cout << "Enter the number you want to cube: ";
string user;
cin >> user;
try
{
// The following instruction tries to parse a double from the 'user' string.
// If the parsing fails, it raises an exception of type bad_lexical_cast.
// If an exception is raised within a try{ } block, the execution proceeds
// with one of the following catch() blocks
double d = lexical_cast <double> (user);
cout << "The cube of " << d << " is " << cube(d) << "." << endl;
break;
}
catch(bad_lexical_cast &e)
{
// This code is executed if the lexical_cast raised an exception; We
// put an error message and continue with the loop
cout << "The inserted string was not a valid double!" << endl;
}
}
return 0;
}
double cube (double n)
{
return n*n*n;
}
Upvotes: 7
Reputation: 90062
sscanf can do what you want; it returns the number of arguments properly processed. This should get you started:
//cubes a user entered number
#include <iostream>
#include <cstdio>
using namespace std;
double cube(double n); //function prototype
int main()
{
cout << "Enter the number you want to cube: "; //ask user to input number
string user;
cin >> user; //user entering the number
// Convert the number to a double.
double value;
if(sscanf(user.c_str(), "%lf", &value) != 1)
{
cout << "Bad! " << user << " isn't a number!" << endl;
return 1;
}
cout << "The cube of " << user << " is " << cube(user) << "." << endl; //displaying the cubed number
return 0;
}
double cube (double n) //function that cubes the number
{
return n*n*n; // cubing the number and returning it
}
Other methods posted in other answers have their advantages and disadvantages. This one has issues with trailing characters and isn't "C++"-y.
Upvotes: 1
Reputation: 2829
You can fall back on C and use strtod
You program reads in a string and then passes it to a function that attempts to convert the string into double.
bool is_double(const char* strIn, double& dblOut) {
char* lastConvert = NULL;
double d = strtod(strIn, &lastConvert);
if(lastConvert == strIn){
return false;
} else {
dblOut = d;
return true;
}
}
Upvotes: 0
Reputation: 979
I would have to say I just started and don't have the slightest of clue about your code, but I will check out your link. By the way, I haven't learned how to work with templates yet,I am learning about dealing with data, only Chapter 3 in my C++ Primer Plus 5th edition.
Upvotes: 0