Question

As an exercise, I'd like to write a macro which tells me if an integer variable is signed. This is what I have so far and I get the results I expect if I try this on a char variable with gcc -fsigned-char or -funsigned-char.

#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0) 

Is this portable? Is there a way to do this without destroying the value of the variable?

Was it helpful?

Solution

#define ISVARSIGNED(V)  ((V)<0 || (-V)<0 || (V-1)<0)

doesn't change the value of V. The third test handles the case where V == 0.

On my compiler (gcc/cygwin) this works for int and long but not for char or short.

#define ISVARSIGNED(V) ((V)-1<0 || -(V)-1<0)

also does the job in two tests.

OTHER TIPS

If you're using GCC you can use the typeof keyword to not overwrite the value:

#define ISVARSIGNED(V) ({ typeof (V) _V = -1; _V < 0 ? 1 : 0 })

This creates a temporary variable, _V, that has the same type as V.

As for portability, I don't know. It will work on a two's compliment machine (a.k.a. everything your code will ever run on in all probability), and I believe it will work on one's compliment and sign-and-magnitude machines as well. As a side note, if you use typeof, you may want to cast -1 to typeof (V) to make it safer (i.e. less likely to trigger warnings).

#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0))

Without destroying the variable's value. But doesn't work for 0 values.

What about:

#define ISVARSIGNED(V) (((V)-(V)-1) < 0)

A different approach to all the "make it negative" answers:

#define ISVARSIGNED(V) (~(V^V)<0)

That way there's no need to have special cases for different values of V, since ∀ V ∈ ℤ, V^V = 0.

This simple solution has no side effects, including the benefit of only referring to v once (which is important in a macro). We use the gcc extension "typeof" to get the type of v, and then cast -1 to this type:

#define IS_SIGNED_TYPE(v)   ((typeof(v))-1 <= 0)

It's <= rather than just < to avoid compiler warnings for some cases (when enabled).

Why on earth do you need it to be a macro? Templates are great for this:

template <typename T>
bool is_signed(T) {
    static_assert(std::numeric_limits<T>::is_specialized, "Specialize std::numeric_limits<T>");
    return std::numeric_limits<T>::is_signed;
}

Which will work out-of-the-box for all fundamental integral types. It will also fail at compile-time on pointers, which the version using only subtraction and comparison probably won't.

EDIT: Oops, the question requires C. Still, templates are the nice way :P

A distinguishing characteristic of signed/unsigned math is that when you right shift a signed number, the most significant bit is copied. When you shift an unsigned number, the new bits are 0.

#define HIGH_BIT(n) ((n) & (1 << sizeof(n) * CHAR_BITS - 1))
#define IS_SIGNED(n) (HIGH_BIT(n) ? HIGH_BIT(n >> 1) != 0 : HIGH_BIT(~n >> 1) != 0

So basically, this macro uses a conditional expression to determine whether the high bit of a number is set. If it's not, the macro sets it by bitwise negating the number. We can't do an arithmetic negation because -0 == 0. We then shift right by 1 bit and test whether sign extension occurred.

This assumes 2's complement arithmetic, but that's usually a safe assumption.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top