Validating std::cin in C++ as float [duplicate]

I know this has been posted a million times because it's like a day 1 problem for any programmer learning C++, but I haven't seen any answers I could understand. Here's what I'm trying to do:

  1. I want to prompt the user to enter a number using std::cin (this part works)
  2. It should give an error and retry if someone enters text like "asdf" (this part works)
  3. It should give an error and retry if the number is less than or equal to 0 (this part works)
  4. It should give an error and retry if someone enters a number with text like "45.7asdf" (this saves as 45.7 with no error)

Here is the code:

    void ClearCin()
    {
        // a standard way of clearing errors caused by invalid user input
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }    

    float startBalance;
    std::cout << "Please enter the starting balance: ";
    std::cin >> startBalance;
    
    // ask for input until a valid float > 0 is entered
    while ((std::cin.fail()) or (startBalance <= 0))
    {
        ClearCin(); //this is a combination of std::cin.clear() and std::cin.ignore()
        std::cout << "Error: starting balance must be a positive number.\n";
        std::cout << "Starting balance is currently set to " << startBalance << "\n";
        std::cout << "Please enter the starting balance: ";
        std::cin >> startBalance;
    }
    std::cout << "Starting balance has been set to " << startBalance << "\n";

How do I make C++ recognize "45asdf" or "43.1asdf45.1" as garbage answers? In the above code, it will recognize "45asdf" as "45", which is valid. It sees "43.1asdf45.1" as "43.1" which is also valid. It seems to grab as many numbers as it can, as long as those numbers come before letters, and then it discards the rest without any errors. Am I missing something trivial or is this a much more complicated problem than I think it is?


Always get user input as a string, and then try to convert it to your desired datatype. Complain if it fails. After converting you can also test for bounds errors.

Remember, the user will always press Enter after every input you ask for.

#include <ciso646>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>

template <typename T> auto string_to( const std::string& s )
{
  T value;
  return (std::istringstream( s ) >> value >> std::ws).eof()
    ? value
    : std::optional <T> {};
}

int main()
{
  // Ask user for input
  std::cout << "What is your age? ";

  // Get the user’s response
  std::string s;
  getline( std::cin, s );

  // Attempt to convert user’s response to a valid value
  auto age = string_to <int> ( s );
  if (!age or (*age < 0)) // ...and validate it
  {
    std::cerr << "That is not a valid age.\n";
    return 1;
  }

  // Success! Use the answer!
  std::cout << "Good job! You are " << *age << " years old!\n";
}

It is often helpful to make a function to handle specific user inputs, like:

int ask_for_integer( const std::string & prompt, int min, int max )

If you are certain that the user is a human (use isatty()) then you can loop until he/she enters a valid input. Otherwise just rage quit and make them run the program again.

Sorry, fixed typos