2

Why are there _BIT macros in limits.h for CHAR_BIT, LONG_BIT, WORD_BIT? Why doesn't it define an INT_BIT?

Is there a reason why these macros are defined for other types, but not for int? Are these deprecated (out of use) with sizeof?

I see these are defined by POSIX for limits.h

Iguananaut
  • 21,810
  • 5
  • 50
  • 63
Evan Carroll
  • 78,363
  • 46
  • 261
  • 468

2 Answers2

4

It looks like WORD_BIT is what you want to be INT_BIT, from the document you link to:

{WORD_BIT} Number of bits in an object of type int.

Note that CX means this is an extension to standard C.

The C99 rationale document tells us the committee saw little reason for anything besides CHAR_BIT:

The macro CHAR_BIT makes available the number of bits in a char object. The C89 Committee saw little utility in adding such macros for other data types.

likely because CHAR_BIT*sizeof(type) does what you need.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 2
    Except it does not work with conditional compilation. –  Sep 18 '18 at 23:36
  • @Ivan can you provide an example where you can't create a macro you need from standard C? It does not change the answer since that is the rationale provided and that won't change but I don't see it. – Shafik Yaghmour Sep 18 '18 at 23:40
  • 1
    Preprocessor cannot evaluate `sizeof` operator. `#if CHAR_BIT*sizeof(type)` won't be able to go through preprocessor stage. –  Sep 19 '18 at 05:03
  • @Ivan so why not just `#define BLAH CHAR_BIT*sizeof(type)` then and use that? – Shafik Yaghmour Sep 19 '18 at 07:12
  • 2
    My point is that this cannot be used with conditional compilation, for example when there is a need to select some datatype at compile time. –  Sep 19 '18 at 08:46
  • Except in the very theoretical scenario when `type` has padding bits. – Petr Skocik Nov 10 '18 at 14:38
1

You can generate them at build time by counting the bits in the corresponding unsigned maximum.

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int popcnt(unsigned long long X) { int pc=0;for(;(X&1);pc++,X>>=1){;} return pc; }
int main()
{
    if(0>printf("#define CHAR_BIT %d\n", popcnt(UCHAR_MAX))) return EXIT_FAILURE;
    if(0>printf("#define UCHAR_BIT %d\n", popcnt(UCHAR_MAX))) return EXIT_FAILURE;
    if(0>printf("#define SCHAR_BIT %d\n", popcnt(UCHAR_MAX))) return EXIT_FAILURE;
    if(0>printf("#define SHRT_BIT %d\n", popcnt(USHRT_MAX))) return EXIT_FAILURE;
    if(0>printf("#define USHRT_BIT %d\n", popcnt(USHRT_MAX))) return EXIT_FAILURE;
    if(0>printf("#define INT_BIT %d\n", popcnt(UINT_MAX))) return EXIT_FAILURE;
    if(0>printf("#define UINT_BIT %d\n", popcnt(UINT_MAX))) return EXIT_FAILURE;
    if(0>printf("#define LONG_BIT %d\n", popcnt(ULONG_MAX))) return EXIT_FAILURE;
    if(0>printf("#define ULONG_BIT %d\n", popcnt(ULONG_MAX))) return EXIT_FAILURE;
    if(0>printf("#define LLONG_BIT %d\n", popcnt(ULLONG_MAX))) return EXIT_FAILURE;
    if(0>printf("#define ULLONG_BIT %d\n", popcnt(ULLONG_MAX))) return EXIT_FAILURE;
    if(0>fclose(stdout)) return EXIT_FAILURE;
    return EXIT_SUCCESS;
}

On my platform, the above gets me:

#define CHAR_BIT 8
#define UCHAR_BIT 8
#define SCHAR_BIT 8
#define SHRT_BIT 16
#define USHRT_BIT 16
#define INT_BIT 32
#define UINT_BIT 32
#define LONG_BIT 64
#define ULONG_BIT 64
#define LLONG_BIT 64
#define ULLONG_BIT 64

(Note that these are not technically guaranteed to be sizeof(the_type)*CHAR_BIT as the C standard allows for padding bits in the native types.)

If running this piece of code before before building on any platform you want to support is annoying to you, I get you.

Using this little shell script, you can generate (once for every platform) a perfectly integer-constant-expression-preserving no-UB-invoking (hopefully) popcnt cpp-macro for up to N-bit integers:

#!/bin/sh -eu
gen()
{
    Max=$1
    local i=0;
    prev='(X)'
    #continuous popcount at compile time
    printf '#define contpopcnt(X) %s\n' "(($prev&1)\\"
    i=1; while [ $i -le $Max ]; do
        #look 1 bit ahead the left shift never reaches or exceeds 
        #the width of whatever X's type might be (would be UB)
        prev="(($prev&2)?((X)>>$i):0)"
        printf '+(%s&1)\\\n' "$prev"
    i=$((i+1));done
    printf ')\n'
}

gen 256
cat<<EOF
//#define CHAR_BIT contpopcnt(UCHAR_MAX)
#define UCHAR_BIT contpopcnt(UCHAR_MAX)
#define SCHAR_BIT contpopcnt(UCHAR_MAX)
#define SHRT_BIT contpopcnt(USHRT_MAX)
#define USHRT_BIT contpopcnt(USHRT_MAX)
#define INT_BIT contpopcnt(UINT_MAX)
#define UINT_BIT contpopcnt(UINT_MAX)
#define LONG_BIT contpopcnt(ULONG_MAX)
#define ULONG_BIT contpopcnt(ULONG_MAX)
#define LLONG_BIT contpopcnt(ULLONG_MAX)
#define ULLONG_BIT contpopcnt(ULLONG_MAX)

//a little sanity check
#include <limits.h>
#include <stdint.h>
#if CHAR_BIT!=contpopcnt(UCHAR_MAX)
    #error "oops"
#endif
EOF

And the 256-bit variant of the macro is only (!) 605KB. So you don't really need those *_BIT macros.

Edit:

A much more compact way over here: https://stackoverflow.com/a/4589384/1084774.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142