2

I want a function that will interpret a string as a strictly unsigned integer, failing when the string overflows, represents a negative number, or does not represent a number.

strtoull() does set errno to ERANGE on overflow or EINVAL for a string that does not begin with a number, but accepts negative numbers as valid.

strtoumax() is the same as strtoull()

atoll() and strtonum() are ruled out because they are strictly for signed integers.

sscanf() sets errno to ERANGE on overflow and stops processing the string if it does not begin with a number (reflected in sscanf's return value), but also accepts negative numbers as valid.

Is there another option that I'm missing?

2
  • You can always manually test the presence of a starting - in the input then use some strtoull Commented Jul 29, 2014 at 5:33
  • Of course I can, the point was to avoid rewriting the 30,000th new version of the same function. Commented Jul 31, 2014 at 18:41

3 Answers 3

3

There are no out-of-the-box solutions that fit your requirements, but it is easy enough to write such a function yourself, using strtoull or strtoll as basis.

For negative numbers, either you can check for the minus sign beforehand, or you can convert to a signed number first and report an error for negative values.
For numbers followed by other characters, you can check that the endp pointer that you pass to strto(u)ll points to the end of the string.

4
  • Converting to a signed number and then rejecting negative values would mean also rejecting half of the possible unsigned values, and for no good reason. Commented Jul 31, 2014 at 18:44
  • @Polyergic: That depends on the range of values you want to support. If you only need to support 32-bit integers, you can convert as 64-bit and still support the full 32-bit unsigned range. Commented Jul 31, 2014 at 20:02
  • I want to support the values that I can store in a 64-bit unsigned integer. If 32-bits were enough I'd've used strtoul rather than strtoull. When I get back to the project I needed this for I'll post my implementation. Commented Aug 1, 2014 at 16:20
  • @Polyergic: If you want to support the full range of the largest unsigned type, then testing for negative values after conversion is clearly not a viable option. That leaves testing the sign beforehand. Commented Aug 1, 2014 at 18:03
2

This should work, unless I'm missing something. Define yourself these thin wrappers around standard C functions:

inline uintmax_t
strtoumax_noneg(const char *numstr, char **restrict endptr, int base)
{
    if (strtol(numstr, endptr, base) < 0) {
        errno = ERANGE;
        return 0;
    }
    return strtoumax(numstr, endptr, base);
}


inline unsigned long long
strtoull_noneg(const char *numstr, char **restrict endptr, int base)
{
    if (strtol(numstr, endptr, base) < 0) {
        errno = ERANGE;
        return 0;
    }
    return strtoull(numstr, endptr, base);
}


inline unsigned long
strtoul_noneg(const char *numstr, char **restrict endptr, int base)
{
    if (strtol(numstr, endptr, base) < 0) {
        errno = ERANGE;
        return 0;
    }
    return strtoul(numstr, endptr, base);
}

If strtol(3) underflows, it returns LONG_MIN, which is < 0, so this rejects all negative integers.

(Edited to add strtoull_noneg() after @ShadSterling 's comment.)

5
  • This is not a stricter strtoull in at least 2 ways: the return type doesn't match, and it will fail on inputs greater than the unsigned max (e.g. "9000000000"). It's also not in any ubiquitous library Commented Dec 12, 2023 at 0:21
  • 1
    @ShadSterling sorry, I misread. I've now added strtoull_noneg(), which is the stricter version of strtoull(3). This is probably the best you'll get from a library, and since both calls are in libc, and this is just a thin wrapper, it shouldn't be very problematic. If you want it in a library, you could add it yourself. Commented Dec 12, 2023 at 12:03
  • I've also added strtoumax_noneg(), just for completeness. Commented Dec 12, 2023 at 12:08
  • I've also clarified that these inline functions should be defined, as they're not provided by any library. Commented Dec 12, 2023 at 12:12
  • Yeah, the motivation was largely to figure out if there was a library function I should use rather than writing one myself, to which the answer is no, but it's good to have this example searchable here Commented Dec 13, 2023 at 22:19
1

You are describing two separate problems. One is that the string should be in a specific form, namely all digits (but you didn't specify whether to permit leading and trailing spaces). The second is that the range should not overflow. There is no readily available function that provides exactly this combination.

The first requirement is best satisfied by testing (or parsing) the string before decoding it. In most languages I would use a regular expression. In C++ you can use library functions. In C you write loops and test characters.

The second requirement is actually quite tricky. You can include overflow testing in your decode function but testing against MAXUINT or something similar needs some careful coding. A simple solution is to decode as a double and then test the range before casting to uint.

In summary, when you have requirements as specific as this you normally finish up writing your own functions, perhaps basing it on available "free" source code. Over the 30 years I've been writing C code I've probably written this function or similar about 5 or 6 times. It gets easier with practice!

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.