Question

I'd like to define a C macro

#define TO_UNSIGNED(x) (...)

, which takes a signed integer x (can be: signed char, short, int, long, long long, or anything else, even something longer than a long long), and it converts x to the corresponding unsigned integer type of the same size.

It's OK to assume that signed integers use the two's complement representation. So to convert any value (positive or negative), its two's complement binary representation should be taken, and that should be interpreted as an unsigned integer of the same size.

I'm assuming that a reasonably modern, optimizing compiler is used which can eliminate unused branches, e.g. if sizeof(X) < 4 ? f(Y) : g(Z) is executed, then X is not evaluated, and only one of f(Y) or g(Z) is generated and evaluated.

Was it helpful?

Solution 4

It looks like there is no generic solution which supports integers of all possible sizes.

For a hardcoded list of types, I was able to make it work using __builtin_choose_expr in C and overloaded function in C++. Here is the solution: https://github.com/pts/to-unsigned/blob/master/to_unsigned.h

The relevant C code looks like this:

#define TO_UNSIGNED(x) ( \
    __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned char), (unsigned char)(x), \
    __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), char), (unsigned char)(x), \
    __builtin_choose_expr(sizeof(x) == sizeof(char), (unsigned char)(x), \
    __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned short), (unsigned short)(x), \
    __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), short), (unsigned short)(x), \
    __builtin_choose_expr(sizeof(x) == sizeof(short), (unsigned short)(x), \
    __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned), (unsigned)(x), \ 
    __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), int), (unsigned)(x), \
    __builtin_choose_expr(sizeof(x) == sizeof(int), (unsigned)(x), \
    __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned long), (unsigned long)(x), \
    __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), long), (unsigned long)(x), \
    __builtin_choose_expr(sizeof(x) == sizeof(long), (unsigned long)(x), \
    __extension__ __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), unsigned long long), (unsigned long long)(x), \
    __extension__ __builtin_choose_expr(__builtin_types_compatible_p(__typeof(x), long long), (unsigned long long)(x), \
    __extension__ __builtin_choose_expr(sizeof(x) == sizeof(long long), (unsigned long)(x), \
    (void)0)))))))))))))))) 

Instead of __builtin_choose_expr + __builtin_types_compatible_p, the equivalent _Generic construct can also be used with compilers that support it, starting from C11.

C++11 has std::make_unsigned, and its implementation in libstdc++ explicitly enumerates the integer types it knows about, similar to how my C++ implementation of TO_UNSIGNED does.

OTHER TIPS

I'll bite, but I have to say it's more in the spirit of macro hacking, not because I think such a macro is useful. Here goes:

#include <stdlib.h>
#include <stdio.h>

#define TO_UNSIGNED(x) (                                            \
    (sizeof(x) == 1)                ? (unsigned char) (x) :         \
    (sizeof(x) == sizeof(short))    ? (unsigned short) (x) :        \
    (sizeof(x) == sizeof(int))      ? (unsigned int) (x) :          \
    (sizeof(x) == sizeof(long))     ? (unsigned long) (x) :         \
                                      (unsigned long long) (x)      \
    )

// Now put the macro to use ...

short minus_one_s()
{
    return -1;
}

long long minus_one_ll()
{
    return -1LL;
}

int main()
{
    signed char c = -1;
    short s = -1;
    int i = -1;
    long int l = -1L;
    long long int ll = -1LL;

    printf("%llx\n", (unsigned long long) TO_UNSIGNED(c));
    printf("%llx\n", (unsigned long long) TO_UNSIGNED(s));
    printf("%llx\n", (unsigned long long) TO_UNSIGNED(i));
    printf("%llx\n", (unsigned long long) TO_UNSIGNED(l));
    printf("%llx\n", (unsigned long long) TO_UNSIGNED(ll));

    printf("%llx\n", (unsigned long long) TO_UNSIGNED(minus_one_s()));
    printf("%llx\n", (unsigned long long) TO_UNSIGNED(minus_one_ll()));

    return 0;
}

The macro uses the ternary comparison operator ?: to emulate a switch statement for all known signed integer sizes. (This should catch the appropriate unsigned integers and the typedef'd typed from <stdint.h>, too. It works with expressions. It also accepts floats, although not quite as I'd expect.)

The somewhat convoluted printfs show that the negative numbers are expanded to the native size of the source integer.

Edit: The OP is looking for a macro that returns an expression of the unsigned type of the same length as the source type. The above macro doesn't do that: Because the two alternative values of the ternary comparison are promoted to a common type, the result of the macro will always be the type of the greatest size, which is unsigned long long.

Branches of different types could probably be achieved with a pure macro solution, such that after preprocessing, the compiler only sees one type, but the preprocessor doesn't know about types, so sizeof cannot be used here, which rules out such a macro.

But to my (weak) defense, I'll say that if the value of the unsigned long long result of the macro is assigned to the appropriate unsigned type (i.e. unsigned short for short), the value should never be truncated, so the macro might have some use.

Edit II: Now that I've stumbled upon the C11 _Generic keyword in another question (and have installed a compiler that supports it), I can present a working solution: The following macro really returns the correct value with the correct type:

#define TO_UNSIGNED(x) _Generic((x),           \
    char:        (unsigned char) (x),          \
    signed char: (unsigned char) (x),          \
    short:       (unsigned short) (x),         \
    int:         (unsigned int) (x),           \
    long:        (unsigned long) (x),          \
    long long:   (unsigned long long) (x),     \
    default:     (unsigned int) (x)            \
    )

The _Generic selection is resolved at compile time and doesn't have the overhead of producing intermediate results in an oversized int type. (A real-world macro should probably include the unsigned types themself for a null-cast. Also note that I had to include signed char explicitly, just char didn't work, even though my chars are signed.)

It requires a recent compiler that implements C11 or at least its _Generic keyword, which means this solution is not very portable, though, see here.

You don't need a macro. The conversion happens automatically. E.g.:

int x = -1;
unsigned int y;

y = x;

EDIT

You seem to want a macro that can infer the type of a variable from its name. That is impossible. Macros are run at a stage of compilation where the compiler doesn't have the type information available. So the macro must emit the same code regardless of the variable's type.

At the stage when type information becomes available, the compiler will insist that every expression has a consistent type. But you're asking for code that is inconsistently typed.

The best you can hope for is to supply the type information yourself. E.g.:

#define TO_UNSIGNED(type, name) (unsigned type(name))

Ok, since you intend to use this macro to implicitly convert negative values to their 2's complement counterparts, I think we can address it the following way:

#include "stdio.h"
#include "stdint.h"


#define TO_UNSIGNED(x) ( \
                          (sizeof(x) == 1 ? (uint8_t)x : \
                          (sizeof(x) <= 2 ? (uint16_t)x : \
                          (sizeof(x) <= 4 ? (uint32_t)x : \
                          (sizeof(x) <= 8 ? (uint64_t)x : \
                          x \
                        )))))



int main () {
    char a = -4;
    int b = -4;

    printf ("TO_UNSIGNED(a) = %u\n", TO_UNSIGNED(a));
    printf ("TO_UNSIGNED(b) = %u\n", TO_UNSIGNED(b));
    return 0;
}

Output:

TO_UNSIGNED(a) = 252
TO_UNSIGNED(b) = 4294967292

Of course support for further lengths may be required, I left the > 64bit to just return x itself for now.

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