Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect signedness with a macro?

I am trying to detect if a value of integer type family (char, unsigned char, short, unsigned short, int, ...) is a negative number in C. If possible with a macro that can be compiled with any compliant C compiler (so, no gcc-tricks allowed) and with no warning !

After some time I came with the following:

#define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0)))

I tried it with the following examples:

void
display_result(int arg, int result)
{
  printf("ISNEG(%d) is %stive\n", arg, (result ? "nega" : "posi"));
}

void
display_uresult(unsigned int arg, int result)
{
  printf("ISNEG(%u) is %stive\n", arg, (result ? "nega" : "posi"));
}

int main ()
{
  short shrt =  5;
  short nshrt = -5;
  unsigned short ushrt = 5;

  display_result(shrt, ISNEG(shrt));
  display_result(nshrt, ISNEG(nshrt));
  display_uresult(ushrt, ISNEG(ushrt));

  int ni = -5;
  int i = 5;
  int zero = 0;

  display_result(ni, ISNEG(ni));
  display_result(i, ISNEG(i));
  display_result(zero, ISNEG(zero));
  display_result(~zero, ISNEG(~zero));  // wrong

  unsigned int uzero = 0;
  unsigned int ui = 5;

  display_uresult(uzero, ISNEG(uzero));
  display_uresult(~uzero, ISNEG(~uzero));
  display_uresult(ui, ISNEG(ui));

  long int li = -5;
  unsigned long int uli = 5;

  display_result(li, ISNEG(li));
  display_uresult(uli, ISNEG(uli));

  long long int lli = -5;
  unsigned long long int ulli = 5;

  display_result(lli, ISNEG(lli));
  display_uresult(ulli, ISNEG(ulli));

  return EXIT_SUCCESS;
}

And, the result is:

ISNEG(5) is positive
ISNEG(-5) is negative
ISNEG(5) is positive
ISNEG(-5) is negative
ISNEG(5) is positive
ISNEG(0) is positive
ISNEG(-1) is negative
ISNEG(0) is positive
ISNEG(4294967295) is positive
ISNEG(5) is positive
ISNEG(-5) is negative
ISNEG(5) is positive
ISNEG(-5) is negative
ISNEG(5) is positive

It works quite nice but the problem is that, when compiled with all the warnings (-Wall -Wextra), I get the following messages:

signedness.c: In function ‘main’:
signedness.c:27:3: warning: promoted ~unsigned is always non-zero [-Wsign-compare]
   display_uresult(ushrt, ISNEG(ushrt));
   ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
 #define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0)))
                                                 ^
signedness.c:41:26: note: in expansion of macro ‘ISNEG’
   display_uresult(uzero, ISNEG(uzero));
                          ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
 #define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0)))
                                                 ^
signedness.c:42:27: note: in expansion of macro ‘ISNEG’
   display_uresult(~uzero, ISNEG(~uzero));
                           ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
 #define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0)))
                                                 ^
signedness.c:43:23: note: in expansion of macro ‘ISNEG’
   display_uresult(ui, ISNEG(ui));
                       ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
 #define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0)))
                                                 ^
signedness.c:49:24: note: in expansion of macro ‘ISNEG’
    display_uresult(uli, ISNEG(uli));
                         ^
signedness.c:4:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
 #define ISNEG(X) ((X) && (X-1) && ((X <= 0) && (~X >= 0)))
                                             ^
signedness.c:55:25: note: in expansion of macro ‘ISNEG’
    display_uresult(ulli, ISNEG(ulli));
                          ^

So, my questions are:

  1. Is there a better way to detect that we have a negative variable among all the possible integer types of C language ?

  2. How to get rid of all these warnings without deactivating it (and without using GCC tricks) ?

like image 281
perror Avatar asked Aug 19 '13 23:08

perror


1 Answers

This definition seems to work for me without any warnings being generated:

#define ISNEG(X) (!((X) > 0) && ((X) != 0))

I used your test cases with this macro on IDEONE.

If you are on a system that supports C.11's _Generic selection feature, then you can do something like this (this is kind of simple minded, I am sure it could be simplified by exploiting integral promotion rules):

#define ISNEG(X) \
    _Generic((X), \                 
             char: !((X) > 0) && (X) != 0, \
             signed char: (X) < 0, \
             short: (X) < 0, \
             int: (X) < 0, \
             long: (X) < 0, \
             long long: (X) < 0, \
             float: (X) < 0, \
             double: (X) < 0, \
             long double: (X) < 0, \
             default: 0)
like image 64
jxh Avatar answered Sep 29 '22 06:09

jxh