The task description is unclear. On the one hand, you state that you want the program to ask for a float value again, until one is provided. On the other hand, you state that this should only happen when the user enters "y"
or "Y"
, but when the user enters anything else, it should print an error message instead. This is contradictory.
If you want your program to check whether the user enters a certain character, then you must read the input as a string, instead of as a number. I recommend that you use std::getline
for this.
Once you have determined that the user did not enter "Y"
or "y"
, you can use the function std::stof
to convert the string to a number.
When the user doesn't enter a number, I don't understand why you say you want to loop back to the input on "y"
and "Y"
, but want to print an error message instead on all other inputs. However, if that is what you want, then this is how you can implement it:
#include <iostream>
#include <string>
int main()
{
std::string input;
float x;
for (;;) //infinite loop, equivalent to while(1)
{
//prompt user for input
std::cout << "Enter a number: ";
//read one line of input
std::getline( std::cin, input );
if ( !std::cin )
throw std::runtime_error( "unexpected stream error!" );
//check if "y" or "Y" was entered
if ( input == "y" || input == "Y" )
continue;
//attempt to convert input to a number
try
{
x = std::stof( input );
}
catch ( std::invalid_argument )
{
printf( "Unable to convert to number\n" );
break;
}
catch ( std::out_of_range )
{
printf( "Number is out of range\n" );
break;
}
std::cout << "You entered the following number: " << x << "\n";
}
}
This program works as intended (based on your contradictory description). If you enter "y"
or "Y"
, it will loop back to the prompt for user input:
Enter a number: y
Enter a number: y
Enter a number: y
Enter a number: 67.5
You entered the following number: 67.5
If you instead provide a non-number input that is not "y"
or "Y"
, it will print an error message, instead of looping back to the input:
Enter a number: y
Enter a number: y
Enter a number: j
unable to convert to number
This behavior does not make sense, but it appears to be what you are asking for.
This program does have one small problem, though. It will accept 6sdfjloj
as valid input for the number 6
:
Enter a number: 6sdfjloj
You entered the following number: 6
It would probably be more meaningful to reject such input with an error message.
Doing this is also possible, by passing a second argument to std::stof
, in order to determine how many characters were converted. If not all characters were converted, you can reject the input. On the other hand, you may want to accept trailing whitespace characters (as determined by std::isspace
), but reject the input if there are any other trailing characters. This would make sense: Because std::stof
accepts leading whitespace characters, it makes sense to also accept trailing whitespace characters.
In my opinion, it would be more meaningful to demonstrate these programming possibilities with the following task:
The user should instead be prompted with the following message:
"Please enter a number, or enter "q" to quit: "
If the user enters "q"
or "Q"
, the program should exit.
Otherwise, it should determine whether the user entered a valid number. If the input is a valid number, the program should say so and print the number, otherwise it should print an error message. Either way, the program should loop back to the initial prompt.
The solution to this problem would be the following:
#include <iostream>
#include <string>
#include <cctype>
int main()
{
std::string input;
float x;
std::size_t pos;
for (;;) //infinite loop, equivalent to while(1)
{
//prompt user for input
std::cout << "Please enter a number, or enter \"q\" to quit: ";
//read one line of input
std::getline( std::cin, input );
if ( !std::cin )
throw std::runtime_error( "unexpected stream error!" );
//check if "q" or "Q" was entered
if ( input == "q" || input == "Q" )
{
std::cout << "Quitting program!\n";
break;
}
//attempt to convert input to a number
try
{
x = std::stof( input, &pos );
}
catch ( std::invalid_argument )
{
printf( "Unable to convert to number!\n" );
continue;
}
catch ( std::out_of_range )
{
printf( "Number is out of range!\n" );
continue;
}
//make sure that any trailing characters are whitespace,
//otherwise reject input
for ( std::size_t i = pos; input[i] != '\0'; i++ )
{
if ( !std::isspace( static_cast<unsigned char>(input[i]) ) )
{
std::cout << "Unexpected character encountered!\n";
//we cannot use continue here, because that would jump
//to the next iteration of the innermost loop, but we
//want to jump to the next iteration of the outer loop
goto continue_outer_loop;
}
}
std::cout << "Input is valid, you entered the following number: " << x << "\n";
continue_outer_loop:
continue;
}
}
This program has the following output:
Please enter a number, or enter "q" to quit: 67.5
Input is valid, you entered the following number: 67.5
Please enter a number, or enter "q" to quit: 32.1
Input is valid, you entered the following number: 32.1
Please enter a number, or enter "q" to quit: sdfjloj
Unable to convert to number!
Please enter a number, or enter "q" to quit: 6sdfjloj
Unexpected character encountered!
Please enter a number, or enter "q" to quit: q
Quitting program!
As you can see, it now also properly rejects input such as 6sdfjloj
.
Note that this program contains one goto
statement. It was appropriate to use it, in order to jump out of a nested loop. This is considered an acceptable use of goto
. However, you should not use goto
except in rare situations, in which there is no cleaner alternative. So please don't get used to using it.