views:

534

answers:

3
+4  A: 

The best bet is always to read your input as strings. You can then use functions like std::strtod() to test and convert to doubles. Checking if streams have failed and then resetting them is error prone at best, and doesn't give you the possibility of producing good error messages.

For example:

string s;
cin >> s;
char * p;
double d = strtod( s.c_str(), & p );
if ( * p == 0 ) {
   cout << "Read double: " << d << endl;
}
else {
   cout << "Read string: " << s << endl;
}

The pointer 'p' will point to the first character that cannot be converted to a double. How exactly you handle that really depends on your app's logic.

anon
could i get a code snippet of an example of this?
Wallter
+1  A: 

cin >> _double will always get you a double, whether they typed in "42", "0" or "mary had a little lamb". You need to read the user input as a string, then test that string to see if it is a double. sscanf will return 0 if it can't convert the input string to the desired type:

cout << "Please enter a DOUBLE:\n" << endl;
    string s;
cin >> s;
    if( !sscanf(s.c_str(), "%lf", &_double) )
    {
        done = false;
        cout << "Not a number, sparky. Try again." << endl;
        continue;
    }

Also, identifiers with leading underscores like you have are reserved by the language. Don't get in the habit of naming things like _double -- someday, they may not work.

John Dibling
haha yeah the '_' was just to give a more concise example of the source
Wallter
i still need to know how to test for a CHAR too...
Wallter
Actually, you can test the result of `cin >> _double`: `if(cin >> _double) done=true; else cout << "Not a number!" << endl;`
Adam Bowen
The format specifier is wrong, it must be "%lf". Using "%f" stuffs a float in a double. This is kinda like the worst of both.
Hans Passant
@nobugz: Quite right, fixed post
John Dibling
+2  A: 

The problem is that when you read something and cin sees the input can never be a double, it stops reading, leaving the stuff in the buffer that it didn't consume. It will signal failure, which you clear but you won't eat the remaining input that cin didn't eat up. So, the next time the same wrong input is tried to read again, and again...

The problem with the char one is that you have to press the return key to make it process any characters on most terminals (this does not happen if you make your program read from a file, for instance). So if you press y then it won't go out of the read call, until you hit the return key. However, then it will normally proceed and exit the loop.

As others mentioned you are better off with reading a whole line, and then decide what to do. You can also check the number with C++ streams instead of C functions:

bool checkForDouble(std::string const& s) {
  std::istringstream ss(s);
  double d;
  return (ss >> d) && (ss >> std::ws).eof();
}

This reads any initial double number and then any remaining whitespace. If it then hit eof (end of the file/stream), it means the string contained only a double.

std::string line;
while(!getline(std::cin, line) || !checkForDouble(line)) 
  std::cout << "Please enter a double instead" << std::endl;

For the char, you can just test for length 1

std::string line;
while(!getline(std::cin, line) || line.size() != 1) 
  std::cout << "Please enter a double instead" << std::endl;

If you want to read only 1 char and continue as soon as that char was typed, then you will have to use platform dependent functions (C++ won't provide them as standard functions). Look out for the conio.h file for windows for instance, which has the _getch function for this. On unix systems, ncurses provides such functionality.

Johannes Schaub - litb
WOW 'Johannes Schaub - litb', thank you so much! you have no idea how much time you saved me. thank you!
Wallter