C: finding the maximum and minimum type of arithmetic expression

I need to find the maximum and minimum of an arbitrary C expression that has no side effects. The following macros work on my machine. Will they work on all platforms? If not, can they be changed to work? I intend to use them to implement macros like SAFE_MUL(a,b) instead of a*b . SAFE_MUL will include overflow check.

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; } 
+6
source share
2 answers
  • The IS_SIGNED macro IS_SIGNED not tell the truth with unsigned types less than int . IS_SIGNED((unsigned char)1) true for any normal implementation, because the type (unsigned char)1*0 is int , not unsigned char .

Your possible SAFE macros should still tell the truth about whether overflow occurs, since the same whole stocks apply to all arithmetic. But they will tell you whether overflow occurs during multiplication, and not necessarily whether this happens when the user converts the result back to the original type of one of the operands.

Think about it, however, you probably knew that since your macros weren't trying to suggest CHAR_MIN and so on. But other people who find this question in the future may not understand this limitation.

  • There is no single type guaranteed to store all the values ​​that can be evaluated by TYPE_MIN and TYPE_MAX . But you can make TYPE_MAX always evaluate to unsigned long long (and the value always matches that type), and the same with TYPE_MIN and signed long long . This will allow you to use the correct printf format without using your knowledge of whether the expression is signed. Currently, TYPE_MAX(1) is long long , while TYPE_MAX(1ULL) is unsigned long long .

  • Technically, it is allowed for int and long have the same size, but different ranges, due to long with fewer fill bits than int . I doubt any important implementation does this.

+3
source

Just an idea: if you use gcc, you can use the typeof extension:

 #define IS_SIGNED(exp) ((typeof(exp))-1 < 0) #define TYPE_MAX_UNSIGNED(exp) ((typeof(exp))-1) #define TYPE_MAX_SIGNED(exp) ... // i cannot improve your code here 

Edit: You can also check for floating point types:

 #define CHECK_INT(exp) ((typeof(exp))1 / 2 == 0) #define CHECK_INT(exp) (((exp) * 0 + 1) / 2 == 0) // if cannot use typeof #define MY_CONST_1(exp) (1/CHECK_INT(exp)) // Now replace any 1 in code by MY_CONST_1(exp) to cause error for floating-point 
+1
source

Source: https://habr.com/ru/post/900440/


All Articles