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_VALFis returned anderrnois set toERANGE. - If the value is negative and too large,
-HUGE_VALFis returned anderrnois set toERANGE. - If the value underflows the normal range, a small value is returned and
errnois 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;
}
