Reputation: 48068
I was wondering if there was some way in C, to introspect the maximum of a type.
So for example I have a variable called a
which is an unsigned short
...
{
unsigned short a;
long long max = TYPEOF_MAX(a);
/* now max will be USHRT_MAX */
}
{
signed char a;
long long max = TYPEOF_MAX(a);
/* now max will be CHAR_MAX */
}
Where TYPEOF_MAX
is a macro which uses some method to get the range based on in type (which is static).
Other qualifiers...
char/short/int/long/long long/float/double
Note: This is for use with generated code, obviously including <lmits.h>
and using USHRT_MAX
and CHAR_MAX
works fine in almost all cases.
Upvotes: 3
Views: 202
Reputation: 48068
This example uses C11 generics based on @Pedro Henrique A. Oliveira's answer,
Note, it could have more types added (ssize_t size_t intptr_t
... etc).
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <float.h>
#include <stdint.h>
#define TYPEOF_MAX(x) \
_Generic(x, \
bool: 1, \
char: CHAR_MAX, signed char: SCHAR_MAX, unsigned char: UCHAR_MAX, \
signed short: SHRT_MAX, unsigned short: USHRT_MAX, \
signed int: INT_MAX, unsigned int: UINT_MAX, \
signed long: LONG_MAX, unsigned long: ULONG_MAX, \
signed long long: LLONG_MAX, unsigned long long: ULLONG_MAX, \
float: FLT_MAX, double: DBL_MAX)
#define TYPEOF_MIN(x) \
_Generic(x, \
bool: 0, \
char: CHAR_MIN, signed char: SCHAR_MIN, unsigned char: 0, \
signed short: SHRT_MIN, unsigned short: 0, \
signed int: INT_MIN, unsigned int: 0, \
signed long: LONG_MIN, unsigned long: 0, \
signed long long: LLONG_MIN, unsigned long long: 0, \
float: -FLT_MAX, double: -DBL_MAX)
/* change 100 to 1000 - static asserts work! */
_Static_assert(TYPEOF_MAX((char)4) > 100, "example check");
int main(void)
{
short num_short;
int num_int;
double num_double;
signed char num_char;
printf(" %ld max short\n", TYPEOF_MAX(num_short));
printf(" %ld max int\n", TYPEOF_MAX(num_int));
printf(" %f max double\n", TYPEOF_MAX(num_double));
printf(" %ld max char\n", TYPEOF_MAX(num_char));
return 0;
}
Upvotes: 2
Reputation: 93770
If you are willing to make assumptions about number representations rather than get bogged down in standards (that support architectures you're hardly likely to ever care about), you can just stuff ~0
into a variable of your target type (for unsigned types) and then assign that value to max
. For signed (or potentially signed) types you stuff in ~0
and check a < 0
. If so, assume 2's complement and go from there. You've already decided that long long
can hold any value of any of the types you care about, so you can stash a copy of a
before smashing it (assuming this trick has to work given only a
).
I just whipped up a test example to verify that this all reduces to a constant with ordinary optimization (with gcc):
a = ~0;
if (a < 0) // true if a is signed
{
a ^= 1 << (sizeof(a) * 8 - 1); // flip sign bit to get all 1's positive
max = a;
min = -a - 1;
}
else
{
max = a;
min = 0;
}
Upvotes: 0
Reputation: 48068
This is a solution that uses GCC's __builtin_types_compatible_p
, While it works it has the following drawbacks.
Sample code:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <math.h>
#include <float.h>
#define TYPEOF_MAX(x) \
({ \
typeof(x) tmp; \
if (__builtin_types_compatible_p(typeof(x), signed char)) \
tmp = (typeof(x))CHAR_MAX; \
else if (__builtin_types_compatible_p(typeof(x), unsigned char)) \
tmp = (typeof(x))UCHAR_MAX; \
else if (__builtin_types_compatible_p(typeof(x), signed short)) \
tmp = (typeof(x))SHRT_MAX; \
else if (__builtin_types_compatible_p(typeof(x), unsigned short)) \
tmp = (typeof(x))USHRT_MAX; \
else if (__builtin_types_compatible_p(typeof(x), signed int)) \
tmp = (typeof(x))INT_MAX; \
else if (__builtin_types_compatible_p(typeof(x), unsigned int)) \
tmp = (typeof(x))UINT_MAX; \
else if (__builtin_types_compatible_p(typeof(x), signed long)) \
tmp = (typeof(x))LONG_MAX; \
else if (__builtin_types_compatible_p(typeof(x), unsigned long)) \
tmp = (typeof(x))ULONG_MAX; \
else if (__builtin_types_compatible_p(typeof(x), float)) \
tmp = (typeof(x))FLT_MAX; \
else if (__builtin_types_compatible_p(typeof(x), double)) \
tmp = (typeof(x))DBL_MAX; \
else \
abort (); \
tmp; \
})
int main(void)
{
short num_short;
int num_int;
double num_double;
signed char num_char;
printf(" %ld max short\n", TYPEOF_MAX(num_short));
printf(" %ld max int\n", TYPEOF_MAX(num_int));
printf(" %f max double\n", TYPEOF_MAX(num_double));
printf(" %ld max char\n", TYPEOF_MAX(num_char));
return 0;
}
Upvotes: 2
Reputation: 368
I've never did c11, however it does have a feature that could help you, according to wikipedia called "Type-generic expressions". From what I understand, you can do _Generic(a, int: INT_MAX, long: LONG_MAX, default: SOME_MANINGFUL_DEFAULT_VALUE)
. This will inspect the type of a and according to its type, select some expression to evaluate, which will be the result of the type-generic expression.
It's not the best solution, but all you gotta do is have a macro which uses _Generic and deals with all the arithmetic types you're interested in.
GCC 4.9 (https://gcc.gnu.org/wiki/C11Status) seems to support it.
The wikipedia page might explain it better than I did: http://en.wikipedia.org/wiki/C11_(C_standard_revision)
Upvotes: 5