RajSanpui
RajSanpui

Reputation: 12064

Problem with string conversion to number ( strtod )

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.

Upvotes: 7

Views: 18753

Answers (7)

chux
chux

Reputation: 153557

OP's code has issues:

getenv() may return NULL

Consider a null pointer test of the result.

Not certainly a string

 char strEnv[32];
 strncpy(strEnv, getenv("LT_LEAK_START"), 31);

 // strEnv is not certainly a string as it may lack a null character.
 strEnv[31] = 0; // Add

The end pointer of strtod() is useful, yet deserves more testing

 // Conversion problems not detected.  See following.
 double d = strtod(strEnv, NULL);

char *endptr;
errno = 0;
double d = strtod(strEnv, &endptr);
if (d == endptr) {
  return Error_No_conversion;  // Like "", "+", "-.", "abc"
}
// Tolerate trailing white-space as leading space is OK
while (isspace(((unsigned char *)endptr)[0])) {
  endptr++;
}
if (*endptr) {
  return Error_Junk_after_number;  // Like "876 - 5309"
}

// Optional pedantic testing.
if (errno == ERANGE) {
  if (fabs(d) > 1.0) {
    // Usually this is OK to just continue;
    // `d` will have the signed value of HUGE_VAL (DBL_MAX or infinity)
    ; // return Error_Number_too_large;
  } else {
    // Usually this is OK to just continue;
    // `d` will have the signed value of DBL_MIN or 0.0 or some small value
    ; // return Error_Number_too_small;
  }
} else if (errno) {
  // Usually this is OK to just continue;
  ; return Error_Implementation_specific_error;
}

// Success, now use `d`.

So far, this answer does not fail "" nor "123 456".

Upvotes: 0

janneb
janneb

Reputation: 37208

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.

Upvotes: 3

pmg
pmg

Reputation: 108938

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 == ' ')

Upvotes: 18

Johann Gerell
Johann Gerell

Reputation: 25581

  • 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.

Upvotes: 1

log0
log0

Reputation: 10917

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
   ...

Upvotes: 3

David
David

Reputation: 1101

I don't know much about this language but I do know that strtod() will return 0.0 if the input is wrong. Maybe you could use a regular expression to validate the input string is a number.

Upvotes: 0

Gareth McCaughan
Gareth McCaughan

Reputation: 19981

That second argument to strtod, which you've set to NULL, can be a pointer-to-pointer-to-char; the pointer-to-char that it points to will get set to the character after the last thing strtod managed to parse. If that's the end of the string, or at least there's nothing after it but whitespace, then what you had was a number. Otherwise, it was something else.

Upvotes: 0

Related Questions