I need to find the maximum and minimum of an arbitrary C expression which has no side effects. The following macros work on my machine. Will they work on all platforms? If not, can they be modified to work? My intent is to subsequently use these to implement macros like SAFE_MUL(a,b) in place of a*b. SAFE_MUL would include a check for multiplication overflow.
EDIT: the type is cast as suggested by Steve.
#include <stdio.h>
#include <limits.h>
#define IS_SIGNED(exp) (((exp)*0-1) < 0)
#define TYPE_MAX_UNSIGNED(exp) ((exp)*0-1)
#define TYPE_MAX_SIGNED(exp) ( \
sizeof (exp) == sizeof (int) \
? \
INT_MAX \
: \
( \
sizeof (exp) == sizeof (long) \
? \
LONG_MAX \
: \
LLONG_MAX \
) \
)
#define TYPE_MAX(exp) ((unsigned long long)( \
IS_SIGNED (exp) \
? \
TYPE_MAX_SIGNED (exp) \
: \
TYPE_MAX_UNSIGNED (exp) \
))
#define TYPE_MIN_SIGNED(exp) ( \
sizeof (exp) == sizeof (int) \
? \
INT_MIN \
: \
( \
sizeof (exp) == sizeof (long) \
? \
LONG_MIN \
: \
LLONG_MIN \
) \
)
#define TYPE_MIN(exp) ((long long)( \
IS_SIGNED (exp) \
? \
TYPE_MIN_SIGNED (exp) \
: \
(exp)*0 \
))
int
main (void) {
printf ("TYPE_MAX (1 + 1) = %lld\n", TYPE_MAX (1 + 1));
printf ("TYPE_MAX (1 + 1L) = %lld\n", TYPE_MAX (1 + 1L));
printf ("TYPE_MAX (1 + 1LL) = %lld\n", TYPE_MAX (1 + 1LL));
printf ("TYPE_MAX (1 + 1U) = %llu\n", TYPE_MAX (1 + 1U));
printf ("TYPE_MAX (1 + 1UL) = %llu\n", TYPE_MAX (1 + 1UL));
printf ("TYPE_MAX (1 + 1ULL) = %llu\n", TYPE_MAX (1 + 1ULL));
printf ("TYPE_MIN (1 + 1) = %lld\n", TYPE_MIN (1 + 1));
printf ("TYPE_MIN (1 + 1L) = %lld\n", TYPE_MIN (1 + 1L));
printf ("TYPE_MIN (1 + 1LL) = %lld\n", TYPE_MIN (1 + 1LL));
printf ("TYPE_MIN (1 + 1U) = %llu\n", TYPE_MIN (1 + 1U));
printf ("TYPE_MIN (1 + 1UL) = %llu\n", TYPE_MIN (1 + 1UL));
printf ("TYPE_MIN (1 + 1ULL) = %llu\n", TYPE_MIN (1 + 1ULL));
return 0;
}
IS_SIGNEDmacro doesn’t tell the truth with unsigned types smaller thanint.IS_SIGNED((unsigned char)1)is true on any normal implementation, because the type of(unsigned char)1*0isint, notunsigned char.Your eventual
SAFEmacros should still tell the truth about whether overflow occurs, since the same integer promotions apply to all arithmetic. But they’ll tell you whether overflow occurs in the multiplication, not necessarily whether it occurs when the user converts the result back to the original type of one of the operands.Come to think of it, though, you probably knew that already since your macros don’t attempt to suggest
CHAR_MINand so on. But other people finding this question in future might not realise that restriction.There is no single type guaranteed to be able to hold all the values that
TYPE_MINandTYPE_MAXcan evaluate to. But you could makeTYPE_MAXalways evaluate tounsigned long long(and the value always fits in that type), and the same withTYPE_MINandsigned long long. This would allow you to use a correct printf format without using your knowledge of whether the expression is signed. CurrentlyTYPE_MAX(1)is along long, whereasTYPE_MAX(1ULL)is anunsigned long long.Technically it’s permitted for
intandlongto have the same size but different ranges, due tolonghaving fewer padding bits thanint. I doubt that any important implementation does that, though.