For an unsigned arithmetic type, (type)-1
is the maximum value. Since you don't know what the relative size of types is, cast to uintmax_t
:
#define UNSIGNED_TYPE_MAX(t) ((uintmax_t)(t)-1)
if ((uintmax_t)x > UNSIGNED_TYPE_MAX(size_t)) puts("too large");
There is no such shortcut for signed types. In fact, I don't think there's any way of determining the largest value of a signed type in strictly portable C89 or C99, without using the corresponding constant, such as SSIZE_MAX
for ssize_t
. C99 specifies constants for each type designed for arithmetic defined in stdint.h
for the types defined in ISO C. For types defined in POSIX but not in standard C, there are many values in limits.h
; note that they are the limit of what can be valid values for what the type is intended for, rather than the limit of what can fit in the type. For example, if size_t
is a 32-bit type, then SIZE_MAX
is guaranteed to be 232-1, whereas SSIZE_MAX
could be less than 231-1 if the implementation doesn't support any byte count larger than that.
With the added assumption that integers are represented in binary and there are no padding bits, which is safe if you're limiting yourself to POSIX (where CHAR_BIT
is always 8), you can deduce the maximum value by computing the size of the type: there is one sign bit in a signed type, and everything else is a value bit.
#define SIGNED_TYPE_MAX(t) (((uintmax_t)1 << (sizeof(t) * CHAR_BIT - 1)) - 1)
Note that things like “double until it stops growing” or “shove in the bit pattern 0111…111” are dodgy. The C standard says that the behavior is undefined for signed types, and GCC takes advantage of this to perform optimizations on operations on signed types that can result in the wrong value if an overflow happens. For example, it might perform computations in a larger-size register, so that the overflow turns out not to happen.