12

Here is the code

double enter_number()
{
  double number;
  while(1)
  {

        cin>>number;
        if(cin.fail())
        {
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
            cout << "Invalid input " << endl;
        }
        else
        break;
        cout<<"Try again"<<endl;
  }
  return number;
}

My problem is that when I enter something like 1x, then 1 is taken as input without noticing the character that is left out for another run. Is there any way how to make it work with any real number e.g. 1.8?

Bo Persson
  • 88,437
  • 31
  • 141
  • 199
user1426900
  • 123
  • 1
  • 1
  • 5

3 Answers3

31

When cin encounters an input it can't properly read in to the variable specified (such as inputing a character into an integer variable), it goes into an error state and leaves the input in it's buffer.

You have to do several things to properly handle this scenario.

  1. You have to test for this error state.
  2. You have to clear the error state.
  3. You have to either alternatively handle the input data that generated the error state, or flush it out and reprompt the user.

The following code provides one of numerous methods of doing these three things.

#include<iostream>
#include<limits>
using namespace std;
int main()
{

    cout << "Enter an int: ";
    int x = 0;
    while(!(cin >> x)){
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        cout << "Invalid input.  Try again: ";
    }
    cout << "You enterd: " << x << endl;        
}

You could just pass in some large value to cin.ignore like 1000 and it's likely to behave exactly the same for all practical purposes.

You can also test cin after the input attempt and handle it that way, something like if(!cin){//clean up the error} .

Check out the istream reference for other member functions to handle stream state: http://cplusplus.com/reference/iostream/istream/

Shiven
  • 702
  • 1
  • 9
  • 17
  • @Jeff Yes, It flushes the buffer. You can also use it when using cin with getline while taking inputs as cin leaves the '\n' in the buffer and doesn't delete (I believe, not completely sure, maybe it was cin.get) so getline just gets delimited the moment it attaches to the input buffer as it encounters the left over '\n'. – Alpha Mineron Sep 29 '17 at 08:01
  • One thing this fails to stop is when a user enters something like `5a`. With this you'll not detect the error until the next time you try to get input. – NathanOliver Aug 07 '19 at 12:47
14

I would use std::getline and std::string to read the whole line and then only break out of the loop when you can convert the entire line to a double.

#include <string>
#include <sstream>

int main()
{
    std::string line;
    double d;
    while (std::getline(std::cin, line))
    {
        std::stringstream ss(line);
        if (ss >> d)
        {
            if (ss.eof())
            {   // Success
                break;
            }
        }
        std::cout << "Error!" << std::endl;
    }
    std::cout << "Finally: " << d << std::endl;
}
Jonas Gröger
  • 1,436
  • 2
  • 21
  • 34
Jesse Good
  • 48,564
  • 14
  • 115
  • 165
-3
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
int get_int(void);
int main()
{
    puts("Enter a number");
    cout<<"The integer is "<<get_int()<<endl;
    return 0;
}
int get_int(void)
{
    char str[20];
    char* end;
    int num;
    do{
        fgets(str,20,stdin);
        str[strlen(str)-1]='\0';
        num=strtol(str,&end,10);
        if(!(*end))
            return num;
        else
        {
            puts("Please enter a valid integer");
            num=0;
        }
    }while(num==0);
}

This works fine for any integer. It can even check if you enter a space or any other character after the integer. The only problem is that it does not make use of std::cin. But, the problem with std::cin is that it ignores any space character after the integer and happily takes the integer as input.

  • 2
    That code is really bad. It mixes C and C++ for no good reason, imposes a hard-coded line limit, the line which adds a null terminator doesn't work properly with multi-byte newlines, and it can’t be used to enter 0. – Sneftel Sep 20 '20 at 06:00
  • about mixing C and C++, strtol() does exist in C++. – Usama Ayub Sep 21 '20 at 10:38
  • and it can be used to enter 0. You must not have tried it. – Usama Ayub Sep 21 '20 at 10:39
  • about str[strlen(str)-1]='\0'; I needed it because otherwise, str would contain the newline character as the last character and the code wouldn't work. If you think you can do better, please give a better solution. – Usama Ayub Sep 21 '20 at 10:46