Reputation: 8123
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
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
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
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
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