min and max value of data type in C

You'll want to use limits.h which provides the following constants (as per the linked reference):

SCHAR_MIN      : minimum value for a signed char
SCHAR_MAX      : maximum value for a signed char
UCHAR_MAX      : maximum value for an unsigned char
CHAR_MIN       : minimum value for a char
CHAR_MAX       : maximum value for a char
SHRT_MIN       : minimum value for a short
SHRT_MAX       : maximum value for a short
USHRT_MAX      : maximum value for an unsigned short
INT_MIN        : minimum value for an int
INT_MAX        : maximum value for an int
UINT_MAX       : maximum value for an unsigned int
LONG_MIN       : minimum value for a long
LONG_MAX       : maximum value for a long
ULONG_MAX      : maximum value for an unsigned long
LLONG_MIN      : minimum value for a long long
LLONG_MAX      : maximum value for a long long
ULLONG_MAX     : maximum value for an unsigned long long
PTRDIFF_MIN    : minimum value of ptrdiff_t
PTRDIFF_MAX    : maximum value of ptrdiff_t
SIZE_MAX       : maximum value of size_t
SIG_ATOMIC_MIN : minimum value of sig_atomic_t
SIG_ATOMIC_MAX : maximum value of sig_atomic_t
WINT_MIN       : minimum value of wint_t
WINT_MAX       : maximum value of wint_t
WCHAR_MIN      : minimum value of wchar_t
WCHAR_MAX      : maximum value of wchar_t
CHAR_BIT       : number of bits in a char
MB_LEN_MAX     : maximum length of a multibyte character in bytes

Where U*_MIN is omitted for obvious reasons (any unsigned type has a minimum value of 0).

Similarly float.h provides limits for float and double types:

FLT_MIN    : smallest normalised positive value of a float
FLT_MAX    : largest positive finite value of a float
DBL_MIN    : smallest normalised positive value of a double
DBL_MAX    : largest positive finite value of a double
LDBL_MIN   : smallest normalised positive value of a long double
LDBL_MAX   : largest positive finite value of a long double
FLT_DIG    : the number of decimal digits guaranteed to be preserved converting from text to float and back to text
DBL_DIG    : the number of decimal digits guaranteed to be preserved converting from text to double and back to text
LDBL_DIG   : the number of decimal digits guaranteed to be preserved converting from text to long double and back to text

Floating point types are symmetrical around zero, so the most negative finite number is the negation of the most positive finite number - eg float ranges from -FLT_MAX to FLT_MAX.

Do note that floating point types can only exactly represent a small, finite number of values within their range. As the absolute values stored get larger, the spacing between adjacent numbers that can be exactly represented also gets larger.


"But glyph", I hear you asking, "what if I have to determine the maximum value for an opaque type whose maximum might eventually change?" You might continue: "What if it's a typedef in a library I don't control?"

I'm glad you asked, because I just spent a couple of hours cooking up a solution (which I then had to throw away, because it didn't solve my actual problem).

You can use this handy maxof macro to determine the size of any valid integer type.

#define issigned(t) (((t)(-1)) < ((t) 0))

#define umaxof(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | \
                    (0xFULL << ((sizeof(t) * 8ULL) - 4ULL)))

#define smaxof(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | \
                    (0x7ULL << ((sizeof(t) * 8ULL) - 4ULL)))

#define maxof(t) ((unsigned long long) (issigned(t) ? smaxof(t) : umaxof(t)))

You can use it like so:

int main(int argc, char** argv) {
    printf("schar: %llx uchar: %llx\n", maxof(char), maxof(unsigned char));
    printf("sshort: %llx ushort: %llx\n", maxof(short), maxof(unsigned short));
    printf("sint: %llx uint: %llx\n", maxof(int), maxof(unsigned int));
    printf("slong: %llx ulong: %llx\n", maxof(long), maxof(unsigned long));
    printf("slong long: %llx ulong long: %llx\n",
           maxof(long long), maxof(unsigned long long));
    return 0;
}

If you'd like, you can toss a '(t)' onto the front of those macros so they give you a result of the type that you're asking about, and you don't have to do casting to avoid warnings.


Maximum value of any unsigned integral type:

  • ((t)~(t)0) // Generic expression that would work in almost all circumstances.

  • (~(t)0) // If you know your type t have equal or larger size than unsigned int. (This cast forces type promotion.)

  • ((t)~0U) // If you know your type t have smaller size than unsigned int. (This cast demotes type after the unsigned int-type expression ~0U is evaluated.)

Maximum value of any signed integral type:

  • If you have an unsigned variant of type t, ((t)(((unsigned t)~(unsigned t)0)>>1)) would give you the fastest result you need.

  • Otherwise, use this (thanks to @vinc17 for suggestion): (((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)

Minimum value of any signed integral type:

You have to know the signed number representation of your machine. Most machines use 2's complement, and so -(((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)-1 will work for you.

To detect whether your machine uses 2's complement, detect whether ((t)~(t)0U) and (t)(-1) represent the same thing. Update (2022-03-24): (t) should be a signed type. The C99 standard mandates the conversion behavior from a signed integer type to unsigned. Regardless of the object representation of the value -N in a signed integer type, converting -N to unsigned always yields the value of one plus the maximum value of the unsigned type minus N. For example, (unsigned long)(-(N)) equals ULONG_MAX+1-(N).

So, combined with above:

(-(((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)-((((t)~(t)0U)==(t)(-1)))

will give you the minimum value of any signed integral type.

As an example: Maximum value of size_t (a.k.a. the SIZE_MAX macro) can be defined as (~(size_t)0). Linux kernel source code define SIZE_MAX macro this way.

One caveat though: All of these expressions use either type casting or sizeof operator and so none of these would work in preprocessor conditionals (#if ... #elif ... #endif and like).

(Answer updated for incorporating suggestions from @chux and @vinc17. Thank you both.)

Tags:

C

Types