R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215547

Is there any way to compute the width of an integer type at compile-time?

The size of an integer type (or any type) in units of char/bytes is easily computed as sizeof(type). A common idiom is to multiply by CHAR_BIT to find the number of bits occupied by the type, but on implementations with padding bits, this will not be equal to the width in value bits. Worse yet, code like:

x>>CHAR_BIT*sizeof(type)-1

may actually have undefined behavior if CHAR_BIT*sizeof(type) is greater than the actual width of type.

For simplicity, let's assume our types are unsigned. Then the width of type is ceil(log2((type)-1). Is there any way to compute this value as a constant expression?

Upvotes: 19

Views: 6009

Answers (9)

Andreas Wenzel
Andreas Wenzel

Reputation: 25385

The new ISO C23 standard has introduced many new macro constants specifying the width of data types, so that there no longer is any need to compute these values yourself (unless you are using an older version of the standard).

The standard library header limits.h has for example introduced the following new macro constants:

macro constant description
BOOL_WIDTH width of bool
CHAR_WIDTH width of char
SCHAR_WIDTH width of signed char
UCHAR_WIDTH width of unsigned char
SHRT_WIDTH width of short
USHRT_WIDTH width of unsigned short
INT_WIDTH width of int
UINT_WIDTH width of unsigned int
LONG_WIDTH width of long
ULONG_WIDTH width of unsigned long
LLONG_WIDTH width of long long
ULLONG_WIDTH width of unsigned long long
PTRDIFF_WIDTH width of ptrdiff_t
SIZE_WIDTH width of size_t

Also, the standard library header stdint.h has introduced for example the following new macro constants:

macro constant description
INTPTR_WIDTH width of intptr_t
UINTPTR_WIDTH width of uintptr_t
INTMAX_WIDTH width of intmax_t
UINTMAX_WIDTH width of uintmax_t
INT_FAST8_WIDTH width of int_fast8_t
UINT_FAST8_WIDTH width of uint_fast8_t
INT_FAST16_WIDTH width of int_fast16_t
UINT_FAST16_WIDTH width of uint_fast16_t
INT_FAST32_WIDTH width of int_fast32_t
UINT_FAST32_WIDTH width of uint_fast32_t
INT_FAST64_WIDTH width of int_fast64_t
UINT_FAST64_WIDTH width of uint_fast64_t

It also defines the macro constants INT8_WIDTH, UINT8_WIDTH, INT16_WIDTH, UINT16_WIDTH, INT32_WIDTH, UINT32_WIDTH, INT64_WIDTH and UINT64_WIDTH for completeness. However, there is little point in using these macro constants, because the width is already implied by the name of the macro constant.

Upvotes: 0

Doug Royer
Doug Royer

Reputation: 65

I prefer standards, I could only find one standard for C++ (#3 below).

For "C" and "C++", (non-standard?) code:

(1) On Linux, the xxxx_WIDTH macros are defined in limits.h

(2) For windows, XXXX_WIDTH (two underscores before and after), seem to be what works. These are the ones defined. And obviously the unsigned pairs are the same width as their signed ones.

SCHAR_WIDTH SHRT_WIDTH INT_WIDTH LONG_WIDTH LONG_LONG_WIDTH

(3) For C++, I need the same. I just found this:

https://en.cppreference.com/w/c/types/limits

The xxxx_WIDTH values.

Upvotes: 0

Will
Will

Reputation: 75673

A general observation is that if you rely on the width of a data type in your calculations, you should use the explicit width data types defined in <stdint.h> e.g. uint32_t.

Trying to count the bytes in the standard types is begging the question of what your 'portable' code would do in the event of an overflow.

Upvotes: 1

Anonymous Question Guy
Anonymous Question Guy

Reputation: 670

There is a function-like macro that can determine the value bits of an integer type, but only if you already know that type's maximum value. Whether or not you'll get a compile-time constant depends on your compiler but I would guess in most cases the answer is yes.

Credit to Hallvard B. Furuseth for his IMAX_BITS() function-like macro that he posted in reply to a question on comp.lang.c

/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
                  + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))

IMAX_BITS(INT_MAX) computes the number of bits in an int, and IMAX_BITS((unsigned_type)-1) computes the number of bits in an unsigned_type. Until someone implements 4-gigabyte integers, anyway:-)


And credit to Eric Sosman for this [alternate version](http://groups.google.com/group/comp.lang.c/msg/e998153ef07ff04b?dmode=source) that should work with less than 2040 bits: **(EDIT 1/3/2011 11:30PM EST: It turns out this version was also written by Hallvard B. Furuseth)**
/* Number of bits in inttype_MAX, or in any (1<<k)-1 where 0 <= k < 2040 */
#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))

**Remember that although the width of an unsigned integer type is equal to the number of value bits, the width of a signed integer type is one greater (§6.2.6.2/6).** This is of special importance as in my original comment to your question I had incorrectly stated that the IMAX_BITS() macro calculates the width when it actually calculates the number of value bits. Sorry about that!

So for example IMAX_BITS(INT64_MAX) will create a compile-time constant of 63. However, in this example, we are dealing with a signed type so you must add 1 to account for the sign bit if you want the actual width of an int64_t, which is of course 64.

In a separate comp.lang.c discussion a user named blargg gives a breakdown of how the macro works:
Re: using pre-processor to count bits in integer types...

Note that the macro only works with 2^n-1 values (ie all 1s in binary), as would be expected with any MAX value. Also note that while it is easy to get a compile-time constant for the maximum value of an unsigned integer type (IMAX_BITS((unsigned type)-1)), at the time of this writing I don't know any way to do the same thing for a signed integer type without invoking implementation-defined behavior. If I ever find out I'll answer my own related SO question, here:
C question: off_t (and other signed integer types) minimum and maximum values - Stack Overflow

Upvotes: 21

Secure
Secure

Reputation: 4378

You can calculate it at runtime with a simple loop, well-defined and without the danger of UB:

unsigned int u;
int c;

for (c=0, u=1; u; c++, u<<=1);

total_bits   = CHAR_BIT * sizeof(unsigned int);
value_bits   = c;
padding_bits = total_bits - value_bits;

The simplest way would be to check in your unit tests (you have them, right?) that value_bits is identical to your current INT_WIDTH definition.

If you really need to calculate it at compile time, I'd go with one of the given #if-#elif cascades, either testing UINT_MAX or your target system.

What do you need it for? Maybe YAGNI?

Upvotes: 1

Jens Gustedt
Jens Gustedt

Reputation: 78973

First approach, if you know what standard type you have (so your type is no typedef) go with the {U}INT_MAX macros and check against the possible sizes.

If you don't have that, for unsigned types this is relatively easy conceptually. For your favorite type T, just do (T)-1 and do a monster test macro that checks against all possible values with ?:. Since these then are only compile time constant expressions, any decent compiler will optimize that out a leave you with just the value that you are interested in.

This wouldn't work in #if etc, because of the type cast, but this can't be avoided in a simple way.

For signed types this is more complicated. For types at least as wide as int you can hope to do a trick to promote to the corresponding unsigned type and get the width of that type then. But to know whether or not your signed type has just one value bit less or not, no I don't think that there is a generic expression to know that.

Edit: Just to illustrate this a bit, I give some extracts of what you can do to make this approach (for unsigned types) not generate too large expressions in P99 I have something like

#ifndef P99_HIGH2
# if P99_UINTMAX_WIDTH == 64
#  define P99_HIGH2(X)                                         \
((((X) & P00_B0) ? P00_S0 : 0u)                              \
 | (((X) & P00_B1) ? P00_S1 : 0u)                            \
 | (((X) & P00_B2) ? P00_S2 : 0u)                            \
 | (((X) & P00_B3) ? P00_S3 : 0u)                            \
 | (((X) & P00_B4) ? P00_S4 : 0u)                            \
 | (((X) & P00_B5) ? P00_S5 : 0u))
# endif
#endif
#ifndef P99_HIGH2
# if P99_UINTMAX_WIDTH <= 128
#  define P99_HIGH2(X)                                         \
((((X) & P00_B0) ? P00_S0 : 0u)                              \
 | (((X) & P00_B1) ? P00_S1 : 0u)                            \
 | (((X) & P00_B2) ? P00_S2 : 0u)                            \
 | (((X) & P00_B3) ? P00_S3 : 0u)                            \
 | (((X) & P00_B4) ? P00_S4 : 0u)                            \
 | (((X) & P00_B5) ? P00_S5 : 0u)                            \
 | (((X) & P00_B6) ? P00_S6 : 0u))
# endif
#endif

where the magic constants are defined with a sequence of #if at the beginning. There it is important to not to expose too large constants for compilers that can't handle them.

/* The preprocessor always computes with the precision of uintmax_t */
/* so for the preprocessor this is equivalent to UINTMAX_MAX       */
#define P00_UNSIGNED_MAX ~0u

#define P00_S0 0x01
#define P00_S1 0x02
#define P00_S2 0x04
#define P00_S3 0x08
#define P00_S4 0x10
#define P00_S5 0x20
#define P00_S6 0x40

/* This has to be such ugly #if/#else to ensure that the            */
/* preprocessor never sees a constant that is too large.            */
#ifndef P99_UINTMAX_MAX
# if P00_UNSIGNED_MAX == 0xFFFFFFFFFFFFFFFF
#  define P99_UINTMAX_WIDTH 64
#  define P99_UINTMAX_MAX 0xFFFFFFFFFFFFFFFFU
#  define P00_B0 0xAAAAAAAAAAAAAAAAU
#  define P00_B1 0xCCCCCCCCCCCCCCCCU
#  define P00_B2 0xF0F0F0F0F0F0F0F0U
#  define P00_B3 0xFF00FF00FF00FF00U
#  define P00_B4 0xFFFF0000FFFF0000U
#  define P00_B5 0xFFFFFFFF00000000U
#  define P00_B6 0x0U
# endif /* P00_UNSIGNED_MAX */
#endif /* P99_UINTMAX_MAX */
#ifndef P99_UINTMAX_MAX
# if P00_UNSIGNED_MAX == 0x1FFFFFFFFFFFFFFFF
#  define P99_UINTMAX_WIDTH 65
#  define P99_UINTMAX_MAX 0x1FFFFFFFFFFFFFFFFU
#  define P00_B0 0xAAAAAAAAAAAAAAAAU
#  define P00_B1 0xCCCCCCCCCCCCCCCCU
#  define P00_B2 0xF0F0F0F0F0F0F0F0U
#  define P00_B3 0xFF00FF00FF00FF00U
#  define P00_B4 0xFFFF0000FFFF0000U
#  define P00_B5 0xFFFFFFFF00000000U
#  define P00_B6 0x10000000000000000U
# endif /* P00_UNSIGNED_MAX */
#endif /* P99_UINTMAX_MAX */
.
.
.

Upvotes: 3

Christoph
Christoph

Reputation: 169763

Compare the macros from <limits.h> against known max values for specific integer widths:

#include <limits.h>

#if UINT_MAX == 0xFFFF
#define INT_WIDTH 16
#elif UINT_MAX == 0xFFFFFF
#define INT_WIDTH 24
#elif ...
#else
#error "unsupported integer width"
#endif

Upvotes: 7

mouviciel
mouviciel

Reputation: 67879

Usually, size of int is known for a given compiler/platform. If you have macros that identify compiler/platform, then you can use them to conditionnally define INT_WIDTH.

You can take a look at <sys/types.h> and its dependents for examples.

Upvotes: -1

Aaron Digulla
Aaron Digulla

Reputation: 328780

Yes, since for all practical purposes, the number of possible widths is limited:

#if ~0 == 0xFFFF
# define INT_WIDTH 16
#elif ~0 == 0xFFFFFFFF
# define INT_WIDTH 32
#else
# define INT_WIDTH 64
#endif

Upvotes: 0

Related Questions