I came across this question in a forum. The answer is something like this:
#define ISUNSIGNED(a) (a >= 0 && ~a >= 0)
//Alternatively, assuming the argument is to be a type, one answer would use type casts:
#define ISUNSIGNED(type) ((type)0 - 1 > 0)
I have a few questions regarding this.Why do we need to check ~a >= 0? What is the second solution all about? I did not understand the statement: “argument is to be a type”. More importantly the author states that first #define will not work in ANSI C (but will work in K&R C). Why not?
For a signed value which is positive,
a >= 0will be true (obviously) and~a >= 0will be false since we’ve flipped the bits so the sign bit is now set, resulting in a negative value. The entire expression is therefore false.For a signed value which is negative,
a >= 0will be false (obviously) and the rest of the expression will not be evaluated; the overall result for the expression is false.For an unsigned value,
a >= 0will always be true (obviously, since unsigned values can’t be negative). If we flip the bits then~a >= 0is also true, since even with the most significant bit (the sign bit) set to 1, it’s still treated as a positive value.So, the expression returns true if the original value and its bitwise inverse are both positive, i.e. it’s an unsigned value.
This is to be called with a type rather than a value:
ISUNSIGNED(int)orISUNSIGNED(unsigned int), for example.For an
int, the code expands towhich is false, since
-1is not greater than0.For an
unsigned int, the code expands toThe signed
1and0literals in the expression are promoted tounsignedto match the first0, so the entire expression is evaluated as an unsigned comparison.0 - 1in unsigned arithmetic will wrap around resulting in the largest possible unsigned value (all bits set to 1), which is greater than 0, so the result is true.As to why it would work with K&R C, but not ANSI C, maybe this article can shed some light:
I guess that means that when comparing an
unsigned shortto0, for example, the unsigned value is converted to asigned intwhich breaks the behaviour of the macro.You can probably work around this by having
(a-a)which evaluates to either signed or unsigned zero as appropriate, instead of the literal0which is always signed.