Problem with string conversion to number ( strtod )

15,449

Solution 1

The 2nd argument to the strtod function is useful.

char *err;
d = strtod(userinput, &err);
if (*err == 0) { /* very probably ok */ }
if (!isspace((unsigned char)*err)) { /* error */ }

Edit: examples added

The strtod function tries to convert the initial portion of the 1st argument to a double and stops either when there are no more chars, or there is a char that can't be used to make a double.

input         result
----------    ----------------------------
"42foo"       will return 42
              and leave err pointing to the "foo" (*err == 'f')

"     4.5"    will return 4.5
              and leave err pointing to the empty string (*err == 0)

"42         " will return 42
              and leave `err` pointing to the spaces (*err == ' ')

Solution 2

man strtod: If no conversion is performed, zero is returned and the value of nptr is stored in the location referenced by endptr.

char * endptr;
double d = strtod(strEnv, &endptr);
if (strEnv == endptr)
   /* invalid number */
else
   ...

Solution 3

Surely you could do worse than just reading the man page for strtod() and acting upon that. E.g. on my Linux system it says:

RETURN VALUE
       These functions return the converted value, if any.

       If  endptr  is  not  NULL,  a pointer to the character after the last character used in the conversion is stored in the location referenced by
       endptr.

       If no conversion is performed, zero is returned and the value of nptr is stored in the location referenced by endptr.

       If the correct value would cause overflow, plus or minus HUGE_VAL (HUGE_VALF, HUGE_VALL) is returned (according to the sign of the value), and
       ERANGE is stored in errno.  If the correct value would cause underflow, zero is returned and ERANGE is stored in errno.

That pretty much tells you what you need to do in order to handle errors. Also, like Johann Gerell said, you also need to check whether getenv() succeeded; a similar approach works there, i.e. check the man page and write error handling code according to that.

Solution 4

  • First, check the return value of getenv - if it's NULL, then that environment variable doesn't exist.
  • Second, if the return value of getenv isn't NULL, then you have the value, as a string.
  • Third, don't set the char ** endptr parameter of strtod to NULL, but use it to check the validity of the converted value, also check for 0.0.
Share:
15,449
RajSanpui
Author by

RajSanpui

Around 9+ years experience into development C, C++, and Linux domain. Also understand Core-Java and consider it as a secondary skill. Currently, in addition to the developer responsibilities, i am also serving the role of DevOps engineer.

Updated on August 04, 2022

Comments

  • RajSanpui
    RajSanpui over 1 year

    I am using strtod( ) function to extract an environment variable as a string, and then changing it to double using strtod:

    enter code here
     char strEnv[32];
     strncpy(strEnv, getenv("LT_LEAK_START"), 31);
     // How to make sure before parsing that env LT_LEAK_START is indeed a number?
     double d = strtod(strEnv, NULL);
    

    Now i want to make sure that this number entered by user is a number and not a string or special character. How can i make sure of that?

    A code snippet would be of great help.

    Thanks in advance.

  • RajSanpui
    RajSanpui about 13 years
    @pmg: Thanks a ton. But i am not exactly able to understand, what exactly you are trying to do with err?
  • RajSanpui
    RajSanpui about 13 years
    @pmg: So, separately, shall we check all for special characters, etc, isalpha, like that?
  • RajSanpui
    RajSanpui about 13 years
    You haven't made any checks for special characters?
  • log0
    log0 about 13 years
    @kingsmasher1 if there is a special character. It is not a valid number anyway. So the conversion fails and endptr is set to strEnv.
  • RajSanpui
    RajSanpui about 13 years
    @pmg: Thanks a lot for the examples. Then why check for spaces? CAn't we directly check for (*err==0)?
  • pmg
    pmg about 13 years
    The input "4 2" will return 4 and leave foo pointing to " 2". I think that is an invalid number, just checking for 1 space in *err is not enough.
  • RajSanpui
    RajSanpui about 13 years
    @pmg: Thanks, your is the best explanation. Many others were also useful.
  • pmg
    pmg about 13 years
    Your answer is misleading: there may be cases where strEnv != endptr and the number was valid, for example "42 " (space at the end)
  • log0
    log0 about 13 years
    @pgm in fact you always have strEnv != endptr if the number is valid.
  • log0
    log0 about 13 years
    @pmg However I still don't agree with your answer. if you have " foo", *err != 0` and isspace((unsigned char)*err) == true
  • pmg
    pmg about 13 years
    @Ugo: you are correct. My code above is incomplete. It doesn't correctly validate for " foo" or "" (empty string). It was only meant as an example usage of the 2nd argument to strtod().
  • Jonathan Wood
    Jonathan Wood over 4 years
    It doesn't tell you all you need to know at all if you want to know if the string is valid. Returning 0.0 on error is fine unless of course 0.0 is a valid floating point value!
  • janneb
    janneb over 4 years
    @JonathanWood: Sure it does; read the man page more carefully!
  • janneb
    janneb over 4 years
    @JonathanWood: There are many aspects of C and the C standard library that can be considered flawed, but this function isn't particularly high on the list. As for your question, it's right there in the part I quoted: "If no conversion is performed, zero is returned and the value of nptr is stored in the location referenced by endptr."
  • janneb
    janneb over 4 years
    @JonathanWood: As for what a "man page" is, the first search engine hit is en.wikipedia.org/wiki/Man_page , which explains it pretty well. (Also note the question is tagged "linux", so the fact that non-Unix systems might not have man pages isn't particularly relevant here).