The Internet
The Internet

Reputation: 8123

Problems with atof in C

you could say that I am relatively new to C, but I need clarification on a question. I have a char[] that represents a number. If this char[] is longer than LONG_MAX I want to tell the user it is too long. The problem is that when I compare its value to a float, it becomes truncated. Here's what I mean.

int main(int argc, char ** argv) {

  char str[] = argv[1]; /* I set it to 9223372036854775809, only +1 higher than LONG_MAX */ 
  double l = atof(str); 
  double j = LONG_MAX; 

  printf("%lf\n", l); /* This prints 9223372036854775808.000000, which is LONG_MAX ??? WHY?? */
  printf("%lf\n", j); /* This prints same as above, 9223372036854775808.000000 */ 

  printf("%s\n", l > j ? "true" : "false"); /* false */

  return 0; /* what am I doing wrong? */

}

UPDATE:

I tried your iret solution and I still run into the same rounding problem

 j = LONG_MAX; 
  int iret = sscanf (str, "%lf", &l);
  if (iret != 1)
    return 0; /* conversion was bad */
  else {
    if (l > j || l < -(j))
      return 0; /* too small or too large */
  }

  printf("%lf\n", l); 
  printf("%lf\n", j);

  printf("%s\n", l > j ? "true" : "false");

Upvotes: 2

Views: 2474

Answers (4)

starmole
starmole

Reputation: 5068

Your long seems to be 63 bits. A float has 23 significant bits (or 24 with leading 1). Your longs are the exactly the same in the upper 24 bits. So they become the same. A float is basically mantissa*2^exponent. The exponent makes sure that the magnitude still matches. But you only have 23 bits of mantissa. So it becomes top23bitsoflong*2^(numberofremainingbits). This is a bit simplified ignoring leading one and exponent bias.

Upvotes: 0

Brian L
Brian L

Reputation: 3251

I believe the reason why you are getting the value "9223372036854775808.000000" for both your inputs is due to the precision limitations of floating point approximations.

By the way, the value isn't LONG_MAX, but LONG_MAX + 1 -> 2^63.

By definition, the representation of LONG_MAX exactly (as an integer) requires 63 bits of precision. However, in a 64-bit floating point representation like double, there are only 53 bits of precision, since the other bits are required to store the sign and exponent. That's why both LONG_MAX and LONG_MAX + 2 end up being rounded to 9223372036854775808 <=> 2^63.

To handle this case properly, perhaps look at strtol which will set an error code when the input is out of range.

Upvotes: 0

Dietrich Epp
Dietrich Epp

Reputation: 213548

You can check for overflow easily enough with strtol, but it requires a little extra work.

const char *str = ...;
char *e;
long x;

errno = 0;
x = strtol(str, &e, 0);
if (!*str || *e) {
    fprintf(stderr, "invalid number: %s\n", str);
    exit(1);
}
if ((x == LONG_MAX || x == LONG_MIN) && errno == ERANGE) {
    fprintf(stderr, "number too large: %s\n", str);
    exit(1);
}

Now, let's talk about the problem with strtod (or atof, which is just a broken version of strtod).

If you convert 9223372036854775809 to a double, then 9223372036854775808 is correct. A double has 53 bits of precision, and a 64-bit long has, well, 64 bits. As soon as you start working with floating-point numbers you need to be prepared for rounding.

For example, there is round-off error in the following code. Can you spot it?

double x = 0.1;

Footnote: I'm assuming 64-bit long and IEEE double-precision double here.

Upvotes: 1

paulsm4
paulsm4

Reputation: 121799

Per the "man" page for atof:

http://linux.die.net/man/3/atof

The atof() function converts the initial portion of the string pointed to by nptr to double. ... atof() does not detect errors.

Which is why I prefer to use "sscanf" instead:

int iret = sscanf (SOME_TEXT, "%lf", &SOME_DOUBLE);

If "iret != 1", then I know an error occurred, and can take the appropriate action.

IMHO...

PS:

Why aren't you declaring "l" and "j". Naughty naughty! You should always declare your variables, even in FORTRAN and Basic ;)

And why not declare them as float ("%f") or double ("%lf")?

Upvotes: 0

Related Questions