I want to write a function (or macro) that tells whether or not a variable is signed. That is, how to tell if the given variable's type is signed or not, as opposed to its value.
I want to write a function (or macro) that tells whether or not a variable is signed. That is, how to tell if the given variable's type is signed or not, as opposed to its value.
You could create a _Generic macro:
#define is_signed(X) _Generic((X), \
short : true, \
int : true, \
long : true, \
long long : true, \
unsigned short : false, \
unsigned int : false, \
unsigned long : false, \
unsigned long long : false, \
float : true, \
double : true, \
long double : true \
)
You could also delegate to functions if you'd like to do something more complicated. I've made a simple example where is_signed returns a signs object including the signedness of both the type and the value of the supplied variable. I've excluded the parameter names where they are not needed, which will be allowed in the C2x standard. You can add dummy names if you want.
typedef struct { bool type; bool value; } signs;
signs Short(short x) { signs r={true, x < 0}; return r; }
signs Int(int x) { signs r={true, x < 0}; return r; }
signs Long(long x) { signs r={true, x < 0}; return r; }
signs Longlong(long long x) { signs r={true, x < 0}; return r; }
signs UShort(unsigned short) { signs r={false, false}; return r; }
signs UInt(unsigned int) { signs r={false, false}; return r; }
signs ULong(unsigned long) { signs r={false, false}; return r; }
signs ULonglong(unsigned long long) { signs r={false, false}; return r; }
signs Float(float x) { signs r={true, x < 0.f}; return r; }
signs Double(double x) { signs r={true, x < 0.}; return r; }
signs LongDouble(long double x) { signs r={true, x < 0.L}; return r; }
#define is_signed(X) _Generic((X), \
short : Short, \
int : Int, \
long : Long, \
long long : Longlong, \
unsigned short : UShort, \
unsigned int : UInt, \
unsigned long : ULong, \
unsigned long long : ULonglong, \
float : Float, \
double : Double, \
long double : LongDouble \
)(X)
You can do this for explicitly listed types using _Generic:
#include <limits.h>
#include <stdio.h>
int main(void)
{
enum Signedness { Signed, Unsigned, Unknown };
char x;
enum Signedness s = _Generic(x,
char: CHAR_MIN < 0 ? Signed : Unsigned,
signed char: Signed,
unsigned char: Unsigned,
short: Signed,
unsigned short: Unsigned,
int: Signed,
unsigned: Unsigned,
long: Signed,
unsigned long: Unsigned,
long long: Signed,
unsigned long long: Unsigned,
default: Unknown
);
switch (s)
{
case Signed:
printf("The type is signed.\n");
break;
case Unsigned:
printf("The type is unsigned.\n");
break;
case Unknown:
printf("It is not known whether the type is signed or unsigned.\n");
break;
}
}
Here is an imperfect way of doing it:
#define isSigned(x) ((x) < 0 ? 1 : (-(x) < 0 ? 1 : 0))
If the number compares less than 0, its type is obviously signed. Otherwise, it's positive, but if its negation is less than 0, again, it was signed.
But, whoops, it's not that simple, because it's also possible the value is 0! (Thanks, @JohnBollinger.) If it's exactly 0, then if x-1 is negative, it was signed.
#define isSigned(x) ((x) < 0 ? 1 : (x) > 0 ? (-(x) < 0 ? 1 : 0) : ((x) - 1 < 0 ? 1 : 0))
(Unfortunately, the extra clause to handle the x == 0 case tips it over the edge from "barely readable" to "just about completely obfuscated.)
In any case, this assumes the variable does have a value (that is, has been initialized).
Unfortunately, it doesn't work at all for types smaller than int, because in that case, -x is negative even if x was unsigned.
Demonstration:
int main()
{
char c = 1;
unsigned char uc = 1;
signed char sc = 1;
int i = -1;
unsigned u = 1;
float f = 1;
double d = -1;
printf("char: %ssigned\n", isSigned(c) ? "" : "un");
printf("signed char: %ssigned\n", isSigned(sc) ? "" : "un");
printf("unsigned char: %ssigned\n", isSigned(uc) ? "" : "un");
printf("int: %ssigned\n", isSigned(i) ? "" : "un");
printf("unsigned: %ssigned\n", isSigned(u) ? "" : "un");
printf("float: %ssigned\n", isSigned(f) ? "" : "un");
printf("double: %ssigned\n", isSigned(d) ? "" : "un");
}
I present two solutions that work on all integer types, and do not require you to come up with a list of all possible intger types.
This works fine in GCC. It uses the typeof extension.
#define SIGNFIND(x) ((typeof(x))-1 < 1)
This works without using C extensions. However, it seems dangerous since it modifies one of the bytes of your variable (temporarily). Also, it assumes your system uses little endian byte order, but that would be pretty easy to fix (just expand it to set all bits of the variable to 1). Also, if the variable has not been initialized when you run this, you are probably invoking undefined behavior.
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
uint8_t * signfind_ptr;
uint8_t signfind_save;
bool signfind_start(uint8_t * v)
{
signfind_ptr = v;
signfind_save = *signfind_ptr;
*signfind_ptr = 0xFF;
return false;
}
bool signfind_end(bool s)
{
*signfind_ptr = signfind_save;
return s;
}
#define SIGNFIND(x) (signfind_start((uint8_t *)&(x) + sizeof(x) - 1), signfind_end((x) < 1))
int main()
{
char v_char = 0;
unsigned char v_uchar = 0;
int v_int = 0;
uint64_t v_u64 = 0;
printf("char: %d\n", SIGNFIND(v_char));
printf("unsigned char: %d\n", SIGNFIND(v_uchar));
printf("int: %d\n", SIGNFIND(v_int));
printf("uint64_t: %d\n", SIGNFIND(v_u64));
}
Firstly, this is only possible with a macro because function's arguments have a pre-defined type.
If you are passing a variable: #define ISUNSIGNED(a) (a >= 0 && ~a >=0)
If you are passing a type: #define ISUNSIGNED(type) ((type)0 - 1 > 0)
For the first: The essential characteristic of an unsigned value is that is cannot be negative. The characteristic of a signed variable is that complementing it most significant bit will change the sign (under 2's complement). Because the other bits are irrelevant, just change them all using ~. (It works. Just get a sample number, negative or not, and work it out on a notepad.)
The second just uses type casts.