Skip to content

2.1 checking whether a string is a valid number

Tory Bjorklund edited this page May 15, 2013 · 7 revisions

Answer

Use boost::lexical_cast and catch the failed conversion exception:

#include <boost/lexical_cast.hpp>
std::string num;
bool is_valid = true;
try {
    int result = boost::lexical_cast<int>(num);  // or your choice of number type
} catch (boost::bad_lexical_cast const&) {
    is_valid = false;
}

Discussion

The Boost lexical_cast function template uses the standard library's formatted input functions to perform the conversion, and throws bad_lexical_cast in case of failure.

This approach will work, but is not ideal, for several reasons:

  1. The recommended approach for using exceptions is to allow them to propagate to the point where they are meaningful, and to catch them there. For example, the caller of the caller of the current function may be in charge of error handling, and therefore the catch block belongs there.
  2. This conversion method does not test whether the input string may be represented within the range of the supplied type.
  3. This approach is not generic; that is, the type of the input and result are fixed as std::string and int, respectively.

If the user desires to know whether the string is a number of some sort, instead of a specific C++ type, a different approach may be required.

Tory's Discussion

Here is my take on it:

Assumptions:

  • Any integer values are in the range [LONG_MIN,ULONG_MAX] will return true
  • Non-integer values are in base 10 decimal form or exponential notation will return true
  • Decimal values have decimal digits <= LDBL_DIG
  • Decimal values are in the range [LDBL_MIN ,LDBL_MAX]
  • Space prefixed numeric values return true, space suffixed numeric values return false (need to trim string first)

Basic Idea:

  • Use std::stringstream because it already defines this conversion

Challenges:

  • Didn’t want std::stringstream to throw

    Turned off conversion exceptions and checked for good conversion.

  • What type should we convert too?

    1. May be integer value that only fits in an unsigned long
    2. May be negative integer value that only fits in a long
    3. May be very small negative decimal value
    4. May be very small positive decimal value
    5. May be very large positive decimal value

    Attempted multiple conversions with perceived most common usage first then to less common usage until conversion is successful.

  • if the first characters are numeric the conversion will still succeed so need to check for eof to avoid “10 Dollars” being a reported as a valid number.

Code:

template <typename T>
bool StringToNumber ( const std::string& text, T& ret )
{
  std::istringstream ss(text);
  // ignore white space prefix
  ss.setf(std::ios::skipws);
  // turn off exceptions
  ss.exceptions(0);
  // attempt conversion extraction
  ss >> ret;
  // make sure we used the entire string and did not fail
  if(ss.eof() && !ss.fail()) return true;
  return false;
}

bool isNumber(const std::string &text)
{
  unsigned long intResult;
  //First check for integer values
  if(StringToNumber(text, intResult)) return true;

  // Next check for negative possibilities
  long intNegResult;
  if(StringToNumber(text, intNegResult)) return true;

  // Try a long double
  long double dblResult;
  if(StringToNumber(text, dblResult)) return true;
  return false;
}

Since I turned off exceptions, the stringsream should not throw when encountering an invalid character when attempting to convert to a numeric type. I tested with the following test cases: Test Cases:

  • zero

0, 0.0, -0, +0, -0.0, +0.0

  • valid small positive integers

1, 99

  • valid large negative integers

-1, -88

  • valid large (positive) integer

ULONG_MAX

  • valid small (negative) integer

LONG_MIN

  • valid small positive decimal value

1 - LDBL_EPSILON (this one didn't work as I expected)

  • valid small negative decimal value

LDBL_MIN

  • valid large decimal value

LDBL_MAX

  • space padded

“ 0 “, “ 1 “, “99 “, " 99", “ .0”, “0.123456789 ”,

  • invalid numeric value with multiple signs
  • invalid numeric value with multiple decimal
  • all text
  • all space
  • empty string
  • numeric value followed by text
Clone this wiki locally