12

I would like to see if a string contains a double as its sole contents. In other words, if it could possibly be the output of the following function:

string doubleToString(double num)
{
    stringstream s;
    s << num;
    return s.str();
}
3
  • Check my answer if you needed: stackoverflow.com/a/39813237/4316802 Commented Oct 2, 2016 at 2:15
  • A string is never a double. Commented May 4, 2017 at 14:39
  • wrongusername, is white-space, aside from the final '\n' , after and before the numeric text allowed? Commented Aug 29, 2021 at 4:17

6 Answers 6

13

You want the strtod function.

bool isOnlyDouble(const char* str)
{
    char* endptr = 0;
    strtod(str, &endptr);

    if(*endptr != '\0' || endptr == str)
        return false;
    return true;
}
Sign up to request clarification or add additional context in comments.

13 Comments

If no conversion is performed, zero is returned and the value of nptr is stored in the location referenced by endptr. It appears I cannot utilize this method of detecting doubles if it happens to be 0
if (x) return false; [else] return true; is better expressed as `return !(x);
Chris: it factors the code better: removes a redundant "return", the keywords true and false that are obviously the results of any logical test, as well as the if statement that also implicit in the situation. So, that proves it's more concise. I can not prove it's simpler or better per se - that depends on the programmer maintaining it. Still, after 28 years programming I can assure you the industry's expectation and best practice is that programmers should make the small effort to get their mind around the essential nature of booleans and simplify the code accordingly.
According to the documentation of strdot decimal point is local dependent. So it means that this function is intended to parse decimal number from user input, but is not suitable to parse some well-defined format, for example files. Imagine that you use dot as decimal point, but on Czech Windows is comma default decimal point instead of dot. So strdot will be looking for comma, which is thousand separator in american format. You will parse non decimal and completely different number instead.
@BoundaryImposition: return endptr != str && *endptr == '\0'; is not condensed at the expense of complexity - it's simplified. If you struggle with that more than the if statement in Chris's answer, you shouldn't be programming professionally.
|
6

You can use the Boost lexical_cast to check whether a string contains double or not.

#include <boost/lexical_cast.hpp> 
....
using boost::lexical_cast; 
using boost::bad_lexical_cast; 
....
template<typename T> bool isValid(const string& num) { 
   bool flag = true; 
   try { 
      T tmp = lexical_cast<T>(num); 
   } 
   catch (bad_lexical_cast &e) { 
      flag = false; 
   } 
   return flag; 
} 

int main(){
  // ....
 if (isValid<double>(str))
     cout << "valid double." << endl; 
 else 
     cout << "NOT a valid double." << endl;
  //....
}

7 Comments

-1 for abuse of exceptions. Exceptions should not be used for control flow, only for handling exceptional circumstances.
@Chris: I have, but not enough to adopt a python style. Even so, what's normal in one language is a very poor practice in another. Another example is writing C++ code in OCaml when you should be using list comprehensions.
@Ben Voigt : I think the code is perfectly fine and cleaner than the code snippet posted by Chris. lexical_cast<T>(num); throws an exception on failure. I couldn't think of a better alternative.
@Chris: But really, it's catching an exception to turn it into a return value that's the code smell. One wouldn't find that in python. Either you have a language with lightweight exceptions, and use them to report failure, or heavyweight exceptions, like C++, where return codes are used for anticipated failures.
@Prasoon Saurav: cleaner how?
|
5

You've been offered C-style and boost alternatives, but in the style of your doubleToString implementation:

bool is_double(const std::string& s)
{
    std::istringstream iss(s);
    double d;
    return iss >> d >> std::ws && iss.eof();
}

Here, you're checking iss >> d returns iss, which will only evaluate to true in a boolean context if the streaming was successful. The check for nothing but whitespace before eof() ensures there's no trailing garbage.

If you want to consider leading and trailing whitespace garbage too:

    return iss >> std::nowkipws >> d && iss.eof();

This can be generalised into a boolean-returning test similar to boost's lexical_cast<>...

template <typename T>
bool is_ws(const std::string& s)
{
    std::istringstream iss(s);
    T x;
    return iss >> x >> std::ws && iss.eof();
}

template <typename T>
bool is(const std::string& s)
{
    std::istringstream iss(s);
    T x;
    return iss >> std::noskipws >> x && iss.eof();
}

...
if (is<double>("3.14E0")) ...
if (is<std::string>("hello world")) ...; // note: NOT a single string
                                         // as streaming tokenises at
                                         // whitespace by default...

You can specialise the template for any type-specific behaviours you'd like, such as:

template <>
bool is<std::string>(const std::string& s)
{
    return true;
}

1 Comment

Shouldn't the is be iss in the parts !(is >> c).
2

Or use streams directly:

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

template<typename T>
bool isValid(std::string const& num)
{
    T  value;
    std::stringstream stream(num);
    stream >> value;

    // If the stream is already in the error state peak will not change it.
    // Otherwise stream should be good and there should be no more data
    // thus resulting in a peek returning an EOF
    return (stream) &&
           stream.peek() == std::char_traits<typename std::stringstream::char_type>::eof();
}

int main()
{
    isValid<double>("55");
}

Comments

0

I made a function that might help? It also checks for typos like 12.3.4 or 1,4.

// check if input string is decimal
bool is_decimal_str(string input)
{
    int decimal = 0;
    int index = 0;
    for (auto character : input)
    {
        if (character == '.')
        {
            decimal++;
        }
        else if ((character == '+' || character == '-') && index == 0);
        else if (!isdigit(character))
        {
            return false;
        }

        if (decimal > 1)
        {
            return false;
        }

        index++;
    }
    return true;
}

or

   // check if input string is decimal
    bool is_decimal_str(string input)
    {
        int decimal = 0;
        for (int index = 0; index < input.size(); index++)
        {
            if (input[index] == '.')
            {
                decimal++;
            }
            else if ((input[index] == '+' || input[index] == '-') && index == 0);
            else if (!isdigit(input[index]))
            {
                return false;
            }
    
            if (decimal > 1)
            {
                return false;
            }
        }
        return true;
    }

Comments

-1

Since no one else has mentioned it: the obvious solution is you want to know whether a string has a given syntax is to use regular expressions. I'm not sure that it's the best solution in this case, since the regular expression for a legal double is fairly complex (and thus easy to get wrong). And your specification is somewhat vague: do you want to accept any string which can be parsed (by >> for example) as a legal double, or only strings can be returned by your doubleToString function? If the former, the simplest solution is probably to convert the string to a double, making sure that there is no error and you have consumed all of the characters (Martin's solution, except that it's silly to make it a template until you need to). If the latter, the easiest solution is to reconvert the double you've found back into a string, using your function, and compare the two strings. (Just to make the difference clear: Martin's function will return true for things like "&nbsp;&nbsp;1.0", "1E2", ".00000000001" and "3.14159265358979323846264338327950288419716939937", which your function will never generate.)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.