I have a file containing strings representing float
and uint64_t
values.
I know exactly which string contains float
values and which contains uint64_t
values - that is not the problem I'm facing.
Here is how I convert them to their respective data-type:
char* t, v;
uint64_t cn;
cn = strtoull(t, &v, 10);
char* tt, vv;
float cn2;
cn2 = strtof(tt, vv);
But the problem arises at the following edge-case I want to catch:
Let's say the string for the uint64_t
is "99999999999999999999999999999999999999999999999999"
This can't be represented within 8 bytes and therefore causes an overflow resulting in cn = 18446744073709551615
.
Same problem with the float cn2
.
How can catch this behavior?
CodePudding user response:
strtoull
provides an indication that the value is out of range. Consider this code:
#include <errno.h>
…
errno = 0; // Set error code to zero before call.
unsigned long long x = strtoull(t, &v, 10);
if (errno == ERANGE)
{
// Handle out-of-range error.
}
This will reach the error case if the numeral in t
is too large.
Note that if t
contains a minus sign (but is not too large in magnitude), strtoull
will return a value that is “negated” in the unsigned long long
type (that is, a value wrapped around ULLONG_MAX
1) even though a negative value is out of range of the type; no error indication will be provided. So, if you want to detect all out-of-range cases, you must check t
for a minus sign (possibly after leading white space) with a non-zero return value.
strtof
provides sufficient information to distinguish cases, per C 2018 7.22.1.3 10:
- If the value is positive and too large,
HUGE_VALF
is returned anderrno
is set toERANGE
. - If the value is negative and too large,
-HUGE_VALF
is returned anderrno
is set toERANGE
. - If the value underflows the normal range, a small value is returned and
errno
is set toERANGE
.
CodePudding user response:
According to this strtoull
reference, when an overflow happens it will return ULLONG_MAX
and errno
is set to ERANGE
.
So you should set errno
to zero before the call, and after could check for ULLONG_MAX
and errno == ERANGE
to see if overflow happens.
Similarly with strtof
it will return HUGE_VALF
.
CodePudding user response:
If value less than 0 need detection
strto(u)ll()
converts to at least a 64-bit integer. Plan for future growth where unsigned long long
may exceed 64-bit.
strtoull("-1", ..., ...)
"wraps" and returns ULLONG_MAX
. To detect that consider:
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
uint64_t convert_str_uint64(const char *s, char **endptr, int base) {
errno = 0;
long long ival = strtoll(s, endptr, base);
// Look for negative numbers
if (ival < 0) {
errno = ERANGE;
return 0;
}
// We are done for many positive numbers
if (*endptr > s && ival <= INT64_MAX && errno == 0) {
return (uint64_t) ival;
}
// Input may still be a valid value more than INT64_MAX.
errno = 0;
unsigned long long uval = strtoull(s, endptr, base);
#if ULLONG_MAX > UINT64_MAX
if (uval > UINT64_MAX) {
uval = UINT64_MAX;
errno = ERANGE;
}
#endif
return (uint64_t) uval;
}