Home > database >  Check if a string representing float/unsigned int is too big
Check if a string representing float/unsigned int is too big

Time:07-15

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 and errno is set to ERANGE.
  • If the value is negative and too large, -HUGE_VALF is returned and errno is set to ERANGE.
  • If the value underflows the normal range, a small value is returned and errno is set to ERANGE.

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;
}
  • Related