C++ IsFloat function

2020-02-09 07:19发布

问题:

Does anybody know of a convenient means of determining if a string value "qualifies" as a floating-point number?

bool IsFloat( string MyString )
{
   ... etc ...

   return ... // true if float; false otherwise
}

回答1:

If you can't use a Boost library function, you can write your own isFloat function like this.

#include <string>
#include <sstream>

bool isFloat( string myString ) {
    std::istringstream iss(myString);
    float f;
    iss >> noskipws >> f; // noskipws considers leading whitespace invalid
    // Check the entire string was consumed and if either failbit or badbit is set
    return iss.eof() && !iss.fail(); 
}


回答2:

You may like Boost's lexical_cast (see http://www.boost.org/doc/libs/1_37_0/libs/conversion/lexical_cast.htm).

bool isFloat(const std::string &someString)
{
  using boost::lexical_cast;
  using boost::bad_lexical_cast; 

  try
  {
    boost::lexical_cast<float>(someString);
  }
  catch (bad_lexical_cast &)
  {
    return false;
  }

  return true;
}

You can use istream to avoid needing Boost, but frankly, Boost is just too good to leave out.



回答3:

Inspired by this answer I modified the function to check if a string is a floating point number. It won't require boost & doesn't relies on stringstreams failbit - it's just plain parsing.

static bool isFloatNumber(const std::string& string){
    std::string::const_iterator it = string.begin();
    bool decimalPoint = false;
    int minSize = 0;
    if(string.size()>0 && (string[0] == '-' || string[0] == '+')){
      it++;
      minSize++;
    }
    while(it != string.end()){
      if(*it == '.'){
        if(!decimalPoint) decimalPoint = true;
        else break;
      }else if(!std::isdigit(*it) && ((*it!='f') || it+1 != string.end() || !decimalPoint)){
        break;
      }
      ++it;
    }
    return string.size()>minSize && it == string.end();
  }

I.e.

1
2.
3.10000
4.2f
-5.3f
+6.2f

is recognized by this function correctly as float.

1.0.0
2f
2.0f1

Are examples for not-valid floats. If you don't want to recognize floating point numbers in the format X.XXf, just remove the condition:

&& ((*it!='f') || it+1 != string.end() || !decimalPoint)

from line 9. And if you don't want to recognize numbers without '.' as float (i.e. not '1', only '1.', '1.0', '1.0f'...) then you can change the last line to:

return string.size()>minSize && it == string.end() && decimalPoint;

However: There are plenty good reasons to use either boost's lexical_cast or the solution using stringstreams rather than this 'ugly function'. But It gives me more control over what kind of formats exactly I want to recognize as floating point numbers (i.e. maximum digits after decimal point...).



回答4:

I recently wrote a function to check whether a string is a number or not. This number can be an Integer or Float.

You can twist my code and add some unit tests.

bool isNumber(string s)
{
    std::size_t char_pos(0);

    // skip the whilespaces
    char_pos = s.find_first_not_of(' ');
    if (char_pos == s.size()) return false;


    // check the significand
    if (s[char_pos] == '+' || s[char_pos] == '-') ++char_pos; // skip the sign if exist

    int n_nm, n_pt;
    for (n_nm = 0, n_pt = 0; std::isdigit(s[char_pos]) || s[char_pos] == '.'; ++char_pos) {
        s[char_pos] == '.' ? ++n_pt : ++n_nm;
    }
    if (n_pt>1 || n_nm<1) // no more than one point, at least one digit
        return false;

    // skip the trailing whitespaces
    while (s[char_pos] == ' ') {
        ++ char_pos;
    }

    return char_pos == s.size();  // must reach the ending 0 of the string
}


void UnitTest() {
    double num = std::stod("825FB7FC8CAF4342");
    string num_str = std::to_string(num);

    // Not number
    assert(!isNumber("1a23"));
    assert(!isNumber("3.7.1"));
    assert(!isNumber("825FB7FC8CAF4342"));
    assert(!isNumber(" + 23.24"));
    assert(!isNumber(" - 23.24"));

    // Is number
    assert(isNumber("123"));
    assert(isNumber("3.7"));
    assert(isNumber("+23.7"));
    assert(isNumber("  -423.789"));
    assert(isNumber("  -423.789    "));
}


回答5:

I'd imagine you'd want to run a regex match on the input string. I'd think it may be fairly complicated to test all the edge cases.

This site has some good info on it. If you just want to skip to the end it says: ^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$

Which basically makes sense if you understand regex syntax.



回答6:

[EDIT: Fixed to forbid initial whitespace and trailing nonsense.]

#include <sstream>

bool isFloat(string s) {
    istringstream iss(s);
    float dummy;
    iss >> noskipws >> dummy;
    return iss && iss.eof();     // Result converted to bool
}

You could easily turn this into a function templated on a type T instead of float. This is essentially what Boost's lexical_cast does.



回答7:

You can use the methods described in How can I convert string to double in C++?, and instead of throwing a conversion_error, return false (indicating the string does not represent a float), and true otherwise.



回答8:

C++11 solution using std::stof:

bool isFloat(const std::string& s) {
    try {
        std::stof(s);
        return true;
    } catch(...) {
        return false;
    }
}


回答9:

You could use atof and then have special handling for 0.0, but I don't think that counts as a particularly good solution.



回答10:

This is a common question on SO. Look at this question for suggestions (that question discusses string->int, but the approaches are the same).

Note: to know if the string can be converted, you basically have to do the conversion to check for things like over/underflow.



回答11:

What you could do is use an istringstream and return true/false based on the result of the stream operation. Something like this (warning - I haven't even compiled the code, it's a guideline only):

float potential_float_value;
std::istringstream could_be_a_float(MyString)
could_be_a_float >> potential_float_value;

return could_be_a_float.fail() ? false : true;


回答12:

it depends on the level of trust, you need and where the input data comes from. If the data comes from a user, you have to be more careful, as compared to imported table data, where you already know that all items are either integers or floats and only thats what you need to differentiate.

For example, one of the fastest versions, would simply check for the presence of "." and "eE" in it. But then, you may want to look if the rest is being all digits. Skip whitespace at the beginning - but not in the middle, check for a single "." "eE" etc.

Thus, the q&d fast hack will probably lead to a more sophisticated regEx-like (either call it or scan it yourself) approach. But then, how do you know, that the result - although looking like a float - can really be represented in your machine (i.e. try 1.2345678901234567890e1234567890). Of course, you can make a regEx with "up-to-N" digits in the mantissa/exponent, but thats machine/OS/compiler or whatever specific, sometimes.

So, in the end, to be sure, you probably have to call for the underlying system's conversion and see what you get (exception, infinity or NAN).



回答13:

I would be tempted to ignore leading whitespaces as that is what the atof function does also:

The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes as many characters as possible that are valid following a syntax resembling that of floating point literals, and interprets them as a numerical value. The rest of the string after the last valid character is ignored and has no effect on the behavior of this function.

So to match this we would:

bool isFloat(string s) 
{ 
    istringstream iss(s); 
    float dummy; 
    iss >> skipws >> dummy; 
    return (iss && iss.eof() );     // Result converted to bool 
} 


回答14:

int isFloat(char *s){
  if(*s == '-' || *s == '+'){
    if(!isdigit(*++s)) return 0;
  }
  if(!isdigit(*s)){return 0;}
  while(isdigit(*s)) s++;
  if(*s == '.'){
    if(!isdigit(*++s)) return 0;
  }
  while(isdigit(*s)) s++;
  if(*s == 'e' || *s == 'E'){
    s++;
    if(*s == '+' || *s == '-'){
        s++;
        if(!isdigit(*s)) return 0;
    }else if(!isdigit(*s)){
        return 0;
    }
  }
  while(isdigit(*s)) s++;
  if(*s == '\0') return 1;
  return 0;
}


回答15:

I was looking for something similar, found a much simpler answer than any I've seen;

bool is_float(float val){
    if(val != floor(val)){
        return true;
    }
    else
        return false;
}

or:

auto lambda_isFloat = [](float val) {return (val != floor(val)); };

Hope this helps !

ZMazz



回答16:

I always liked strtof since it lets you specify an end pointer.

bool isFloat(const std::string& str)
{
    char* ptr;
    strtof(str.c_str(), &ptr);
    return (*ptr) == '\0';
}

This works because the end pointer points to the character where the parse started to fail, therefore if it points to a nul-terminator, then the whole string was parsed as a float.

I'm surprised no one mentioned this method in the 10 years this question has been around, I suppose because it is more of a C-Style way of doing it. However, it is still perfectly valid in C++, and more elegant than any stream solutions. Also, it works with "+inf" "-inf" and so on, and ignores leading whitespace.