how to deny non-numeric input?

heres and example of the application :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;

int main()
{
double y;
double x;
cout << "enter a decimal number\n" ;
cin >> x;
modf(x, &y);
cout << "The remainder = " << modf(x, &y) << endl;

if (modf(x, &y) < .5) {
	cout << "the number is rounded down to " << y << endl;
}
else {
	y = y + 1;
		cout << "the number is rounded up to " << y << endl;

}
return main();
}

If I enter anything but a number, of course, it freaks out. How do I easily handle any input that is not a number?
When cin cannot stream data to the variable, like char to a double, it stops and sets a fail flag. If you attempt to use cin while it is in a fail state, nothing will happen.
1
2
3
4
5
6
7
8
9
10
11
12
13
while( ! cin >> x){ //While cin failed to stream the data; Reads input then checks if cin has failed
//Alternative:
/*
cin >> x;
while(cin.fail()){
*/
   cin.clear(); //Reset the flags, so you can use cin again
   cin.ignore(100, '\n'); //Empty the buffer
   cout << "Please enter a number!\n";
/* If alternative is used:
   cin >> x; //Read input again
*/
}


This will not, however protect against input like "23hfff".

Edit:
Just noticed your return line. You should never ever resort to calling main() directly. Use loops instead.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main()
{
    double result = 0; // Edit: was int
    bool   done   = false;

    while (!done)
    {
        cout << "Enter a number : ";

        // get a string from the user
        string line;
        getline(cin, line);

        // use it to init a stringstream
        istringstream is(line);

        // try to read a number from the stringstream...
        // if a number can't be read, or there is more than white
        // space in the string after the number, then fail the read
        // and get another string (number) from the user (so "abc",
        // "42nd Str", "1  2 3", etc are all invalid.)
        char dummy = '\0';
        if (!(is >> result) || (is >> ws && is.get(dummy)))
            cout << "Invalid input. Try again!\n";
        else
            done = true ;
    }

    cout << "Your number is : " << result << '\n';

    return 0;
}


Andy

PS leveraged from cire's reply to this thread:

Trying to limit input to int type
http://www.cplusplus.com/forum/beginner/108849/#msg592118

Edit: tweaked code to work with double rather than int.

Line 8 changed from:

int result = 0;

to

double result = 0; // Edit: was int
Last edited on
@Andy thankyou that looks nice.
But unfortunately it doesn't allow for non-integer numbers.
If you enter a decimal it treats it as non-numeric input and says invaid, try again.
Maybe there is a way to change that?

@Daleth This way works well. I used the while (cin.fail()) rather than while (! cin >> x) because >> gives errors with double

in the cin.ignore(100, '\n') is the 100 the number of characters it's clearing / ignoring?

Also, is there a way to clear the prompt each time I reset it ?
Here is the updated version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
double y;
double x;
int a;

do
{
		a =0;
cout << "enter a decimal number\n" ;
		cin >> x;
		while(cin.fail()) 
		{
		cin.clear();
		cin.ignore(100, '\n');
		cout << "Please enter a number!\n";
		cin >> x;
		}
modf(x, &y);
cout << "The remainder = " << modf(x, &y) << endl;

if (modf(x, &y) < .5) {
	cout << "the number is rounded down to " << y << endl;
}
else {
	y = y + 1;
		cout << "the number is rounded up to " << y << endl;

}
y = y + 1;
cout << "then number + 1 = " << y << endl;
		a = 1;
}while(a =1);
return 0;
}

Any other suggestions?
@Andy thankyou that looks nice.
But unfortunately it doesn't allow for non-integer numbers.
If you enter a decimal it treats it as non-numeric input and says invaid, try again.
Maybe there is a way to change that?


If you look at the post Andy linked to, the original is a template function which doesn't have that limitation as you specify the type expected when calling the function.

[Edit: And you could change the code in Andy's post to handle double or floats by simply changing the type of result.]
Last edited on
I've tweaked my code to use double instead of int (questions are so often about ints I looked through the double...)

As cire has already said, his original function is a templated so can be used for whatever type you like (as long as it understands operator>> and is assignable.) And it even does validation.

I reduced the code a bit so I had less to explain here, leaving the complete version as "further reading.".

Andy
Last edited on
@andy thankyou for the tweak, It works with decimals now.
I appreciate all help.
I just have trouble understanding string line; getline (cin, line); istringstream is(line); and the is !(is >> result)
but I can implement it fine.


Please let me know if this could be better :
It take number 1, then number 2, multiplies number 1 by 2 and divides that by number 2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main()
{
    double x =0;
    double y =0;
    bool   input1 = false;
    bool   input2 = false;
    double z;

    while (!input1)
    {
        cout << "Enter first number: ";
        string line;
        getline(cin, line);
        istringstream is(line);
        char dummy = '\0';
        if (!(is >> x) || (is >> ws && is.get(dummy)))
            cout << "Invalid input. Try again!\n";
        else
            input1 = true ;
    }

	    while (!input2)
    {
        cout << "Enter second number: ";
        string line;
        getline(cin, line);
        istringstream is(line);
        char dummy = '\0';
        if (!(is >> y) || (is >> ws && is.get(dummy)))
            cout << "Invalid input. Try again!\n";
        else
            input2 = true ;
    } 
x = x * 2;
z = x / y;
		cout << "New number =  " << z << endl;

    return 0;
}

So is this completely full-proof way to do this? or in which way could this simple operation be perfected even more?
I have lesser knowledge on declaring variables and how I should do so.
Are the variables introduced done so in the best way?

I'm sorry for so many questions I'm just trying to understand this the best I can.

I just have trouble understanding string line; getline (cin, line); istringstream is(line); and the is !(is >> result)

Have you looked at the help on these calls??

getline (string)
http://www.cplusplus.com/reference/string/string/getline/

istringstream::istringstream
http://www.cplusplus.com/reference/sstream/istringstream/istringstream/

You should have a look at the help and have a go at working out what they do. (Every time you come across a new function or class, you should check it out in this site's help and elsewhere. That way, over time, you will become familiar with the standard library, etc.)

!(is >> result) is a bit more involved than the others.

To start with, cin is an instance of the std::istream.

Checking the help on istream::operator>> we see that all overloads of this method return a reference to the stream object
http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/

istream& operator>> (some_type&);

so the result of is >> result is a reference to the stream (here "is")

Now while istream does not itself define operator!, it inherits fom std::basic_ios which does:

basic_ios::operator!
http://www.cplusplus.com/reference/ios/basic_ios/operator_not/

And this operator returns:
- true if either failbit or badbit is set.
- false otherwise.

so operator! is a way of checking to see if the stream is in an error state. This will happen when you try to reads a non-numeric string into a double variable, etc.

Andy

PS The C++ standard actually states that basic_ios::operator! returns the result of fail(); and that fail() returns true if either failbit or badbit is set in rdstate().

PPS You will also see similar code without the operator! For example,

1
2
3
4
while(is >> result)
{
    cout << "value = " << result << "\n";
}


this works in a similar way, but it works with the istream's operator void*, rather than operator!, which returns non-null if the stream is in an valid state, null otherwise.
Last edited on
And...

Please let me know if this could be better

The main improvement would be to move the input code to a function, so you don't end up with 13 lines for each variable you get from the user. See cire's example for an example. And see (if you haven't already done so):
http://www.cplusplus.com/doc/tutorial/functions/

So is this completely full-proof way to do this?

To the best of my knowledge, yes.

But you could test it?

or in which way could this simple operation be perfected even more?

I think it is about as suscint as you can get.

But why not have a go trying to improve it? Or at least match it??

Are the variables introduced done so in the best way?

In C++ is is seen as good form to declare variables at the point of first use, or as close to it as possible. And in as tight a scope as possible. Declaring variables at the start of a function is old-school C (i.e. C89) style.

Some people (including me) also follow the rules:
- only a single declaration/definition per line
- initialize all variables

There are always exceptions to any rule (for example, I wouldn't usually inititialize a large buffer I was about to use to read file data), but uninitialized variables can cause real grief. So it is safer to make it the general rule that you initialize them, and only omit the initialization with good reason (with code comments to explain why.)

As you should be using declaration at first point of use most of the time anyway, you shouldn't have many variable to initialize before use. So the one-per-line and initialize-all rules aren't actually particularly arduous to comply with.

In your code this means line 40 should become

double z = x / y;

and line 12 deleted.

And lines 9 and 11 could be moved to just before while (!input2) on line 27

And if you move the input code into a function, you could reduce your main down to something like

1
2
3
4
5
6
7
8
9
10
11
int main()
{
    double x = getInput("Enter first number: ");
    double y = getInput("Enter second number: ");

    double z = (2 * x) / y;

    cout << "New number =  " << z << endl;

    return 0;
}


Andy
Last edited on
PS A cheap way to test your app would be to use a text input file with the command line redirection operator.

For example, if you create a text file (e.g. test1.txt) containing

aardvark
 1 2 3 testing
12 feet
  42nd street    
1/3
  1.234
2

open a command prompt and change to the directory where your app is located (e.g. C:\Test)

and then (where I've built your app as "num_only.exe") type in the following command at the prompt:

C:\Test>num_only.exe < test1.txt

you should get the following output in response:

Enter first number: Invalid input. Try again!
Enter first number: Invalid input. Try again!
Enter first number: Invalid input. Try again!
Enter first number: Invalid input. Try again!
Enter first number: Invalid input. Try again!
Enter first number: Enter second number: New number =  1.234

All without having to type in the assorted bad string. As a one off it's more work; but it makes it dead easy to retest with the same data later on, after making changes to your code.

Andy

PS You can take this style of testing a step futher by capturing the ouput using

C:\Test>num_only.exe < test1.txt > output1.txt

or

C:\Test>num_only.exe < test1.txt > output1.txt 2>&1

(the 2>&1 redirects the output of cerr to cout, which is redirected to the file, so everything written to both cout and cerr ends up in the file.)

You could then write another program which reads output1.txt to check the value of the output, etc. Or use a diff tool (fc.exe is the one which is provided with Windows) to compare the new output against an older output file which you'd checked manually earlier.

With the help of a batch file (or shell script, if you're doing the same kind of think on Linux) you could easily run a sequence of such tests automatically.
Last edited on
Topic archived. No new replies allowed.