1

i new to programming and we are required to create a program that dont exit when the user inputs the wrong input, but i only learned the basics so far.. i already solved when the number is above and below 100 but when the user accidentally inserted a non integer it will go into a error loop. btw this is an average calculator.

#include <iostream>

using namespace std;

int main()
{
    int num[100];
    int n;
    double sum, average;

    cout << "how many numbers will you input?: ";
    cin >> n;

    while ( n > 100 || n <= 0 )
    {
        cout << "Error! number should in range of (1 to 100) only." << endl;
        cout << "Enter the number again: ";
        cin >> n;
    }

    for ( int i = 0; i < n; ++i )
    {
        cout << i + 1 << ". Enter number: ";
        cin >> num[i];
        sum += num[i];
    }

    average = sum / n;
    cout << "Average = " << average;
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
ev0l
  • 13
  • 4
  • Does this answer your question? [What is the best way to do input validation in C++ with cin?](https://stackoverflow.com/questions/545907/what-is-the-best-way-to-do-input-validation-in-c-with-cin). Or [Good input validation loop using cin - C++](https://stackoverflow.com/questions/2075898/good-input-validation-loop-using-cin-c) – Jason May 20 '22 at 08:25
  • Read *strings* and attempt to [convert it to an `int`](https://en.cppreference.com/w/cpp/string/basic_string/stol). As for the reading of string, you should probably [read whole lines](https://en.cppreference.com/w/cpp/string/basic_string/getline). – Some programmer dude May 20 '22 at 08:25

2 Answers2

1

If std::istream::operator >> fails, it will set failbit. Therefore, you should check failbit (for example by calling std::cin.fail()) to see whether the conversion was successful, before using the result of the conversion.

If the conversion fails due to bad input, then the next call to std::istream::operator >> will automatically fail due to failbit being set. That is why you are getting stuck in an infinite loop. If you want to attempt input again after a conversion failure, you will first have to clear failbit, by using the function std::cin.clear().

Also, you will have to discard the bad input that caused the conversion to fail, because otherwise, the next time you call std::istream::operator >>, the conversion will fail again for the same reason. In order to clear the bad input, you can use std::cin.ignore(), like this:

std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );

In order to use std::numeric_limits, you will have to #include <limits>.

After performing these fixes on your code, it should look like this:

#include <iostream>
#include <limits>

using namespace std;

int main()
{
    int num[100];
    int n;
    double sum, average;
    bool input_ok;

    //repeat until input is valid
    do
    {
        cout << "How many numbers will you input? ";
        cin >> n;

        if ( cin.fail() )
        {
            cout << "Error: Conversion to integer failed!\n";

            input_ok = false;
        }
        else if ( n > 100 || n <= 0 )
        {
            cout << "Error: Number should in range of (1 to 100) only!\n";
            input_ok = false;
        }
        else
        {
            input_ok = true;
        }

        //clear failbit
        cin.clear();

        //discard remainder of line
        cin.ignore( numeric_limits<streamsize>::max(), '\n' );
    } while ( !input_ok );

    for ( int i = 0; i < n; ++i )
    {
        do
        {
            cout << i + 1 << ". Enter number: ";
            cin >> num[i];

            if ( cin.fail() )
            {
                cout << "Error: Conversion to integer failed!\n";

                input_ok = false;
            }
            else
            {
                input_ok = true;
            }

            //clear failbit
            cin.clear();

            //discard remainder of line
            cin.ignore( numeric_limits<streamsize>::max(), '\n' );
        } while ( !input_ok );

        sum += num[i];
    }

    average = sum / n;
    cout << "Average = " << average;
}

This program has the following behavior:

How many numbers will you input? 200
Error: Number should in range of (1 to 100) only!
How many numbers will you input? -31
Error: Number should in range of (1 to 100) only!
How many numbers will you input? test
Error: Conversion to integer failed!
How many numbers will you input? 4abc
1. Enter number: 1
2. Enter number: 2
3. Enter number: 3
4. Enter number: 4
Average = 2.5

As you can see, the program now works in that it can now handle bad input such as test. It rejects that input and reprompts the user for new input.

However, one problem with this program is that it accepts 4abc as valid input for the number 4. It would probably be appropriate to reject such input instead. One way to fix this would be to inspect the remainder of the line, instead of simply discarding it.

Another issue is that this solution contains a lot of code duplication. Apart from the range check, both do...while loops are nearly identical. Therefore, it would be better to put this loop into a function, which can be called from several places in your code.

However, I generally don't recommend that you use std::istream::operator >>, because its behavior is not always intuitive. For example, as already pointed out above:

  1. It does not always read a whole line of input, so that you must explicitly discard the remainder of the line.
  2. It accepts 4abc as valid input for the number 4.

In my experience, if you want proper input validation of integer input, it is usually better to write your own function that reads a whole line of input using std::getline and converts it with std::stoi. If the input is invalid, then the function should automatically reprompt the user.

In my example below, I am calling this function get_int_from_user.

If you want to additionally ensure that the input is in a certain range, then you can call the function get_int_from_user in an infinite loop, and break out of that loop once you determine that the input is valid.

#include <iostream>
#include <string>
#include <sstream>
#include <cctype>

int get_int_from_user( const std::string& prompt );

int main()
{
    int nums[100];
    int n;
    double sum;

    //repeat loop forever, until input is good
    for (;;) //equivalent to while(true)
    {
        n = get_int_from_user( "How many numbers will you input? " );

        if ( 1 <= n && n <= 100 )
            //input is good
            break;

        std::cout << "Error! Number should in range of (1 to 100) only.\n";
    }

    //read one number per loop iteration
    for( int i = 0; i < n; i++ )
    {
        std::ostringstream prompt;

        prompt << "Enter number #" << i + 1 << ": ";

        nums[i] = get_int_from_user( prompt.str() );

        sum += nums[i];
    }

    std::cout << "Average: " << sum / n << '\n';
}

int get_int_from_user( const std::string& prompt )
{
    std::string line;
    std::size_t pos;
    int i;

    //repeat forever, until an explicit return statement or an
    //exception is thrown
    for (;;) //equivalent to while(true)
    {
        //prompt user for input
        std::cout << prompt;

        //attempt to read one line of input from user
        if ( !std::getline( std::cin, line ) )
        {
            throw std::runtime_error( "unexpected input error!\n" );
        }

        //attempt to convert string to integer
        try
        {
            i = std::stoi( line, &pos );
        }
        catch ( std::invalid_argument& )
        {
            std::cout << "Unable to convert input to number, try again!\n";
            continue;
        }
        catch ( std::out_of_range& )
        {
            std::cout << "Out of range error, try again!\n";
            continue;
        }

        //The remainder of the line is only allowed to contain
        //whitespace characters. The presence of any other
        //characters should cause the entire input to get rejected.
        //That way, input such as "6sdfj23jlj" will get rejected,
        //but input with trailing whitespace will still be accepted
        //(just like input with leading whitespace is accepted by
        //the function std::stoi).
        for ( ; pos < line.length(); pos++ )
        {
            if ( !std::isspace( static_cast<unsigned char>(line[pos]) ) )
            {
                std::cout << "Invalid character found, try again!\n";

                //we cannot use "continue" here, because that would
                //continue to the next iteration of the innermost
                //loop, but we want to continue to the next iteration
                //of the outer loop
                goto continue_outer_loop;
            }
        }

        //input is valid
        return i;

    continue_outer_loop:
        continue;
    }
}

This program has the following behavior:

How many numbers will you input? 200
Error! Number should in range of (1 to 100) only.
How many numbers will you input? -31
Error! Number should in range of (1 to 100) only.
How many numbers will you input? test
Unable to convert input to number, try again!
How many numbers will you input? 4abc
Invalid character found, try again!
How many numbers will you input? 4
Enter number #1: 1
Enter number #2: 2
Enter number #3: 3
Enter number #4: 4
Average: 2.5

As you can see, it now correctly rejects the input 4abc.

I believe that using the function get_int_from_user makes the code in main much cleaner.

Note that the code above uses one goto statement. Under most circumstances, you should avoid using goto, but for breaking out of nested loops, it is considered appropriate.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
-1
 #include <iostream>
 #include <limits>
    using namespace std;
    int avg(int sum, int n)
    {
        int average;
        average = sum/n;
        cout<<"Avg = "<<average;
    }
    int main()
    {
    int n;
    cout << "how many numbers will you input?: ";
    cin >> n;
    int w = n;
    int num[w];
    int sum;
    while(n>100||n<=0)
    {
    cout << "Error! number should in range of (1 to 100) only." << endl;
    cout << "Enter the number again: ";
    cin >> n;
    }
    for(int i = 0; i < n; ++i)
    {
    cout << i + 1 << "Enter number: ";
    cin >> num[i];
    if(!cin)
    {
          cout << "Wrong Choice. " << endl;
          cin.clear();
          cin.ignore( numeric_limits<std::streamsize>::max(), '\n' );
           n++;
          continue;
    }
    else
    {
    sum += num[i];
    }
    }
    cout<<"Sum  = "<<sum<<endl;
    avg(sum,w);
    }
  
  • 3
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 20 '22 at 09:49
  • 2
    Your code would be easier to read if you used consistent [indentation](https://en.wikipedia.org/wiki/Indentation_style). Note that you can [edit] your answer to fix it. – Andreas Wenzel May 20 '22 at 13:38