最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Is there a way to express binary numbers with some sort of separation between digits in C? - Stack Overflow

programmeradmin0浏览0评论

In reality, we often express long binary numbers in the form of 0000 1111 0011 1100 to make reading easier. And very often, especially in dealing with chip-chip communications in embedded systems (like I2C), we have to perform bitwise logical operations (usually bitwise OR) to abstract informations for decisions.

Yet as far as I know, in the C standard, no spacing or any form of separation between digits is allowed, which makes it more difficult to read the long binary numbers. Using hexadecimal number is somehow worse since it masks the binary number.

Is there a conforming way to express long binary numbers with separators (such as 0b0000 1111 0011 1100) in C?

In reality, we often express long binary numbers in the form of 0000 1111 0011 1100 to make reading easier. And very often, especially in dealing with chip-chip communications in embedded systems (like I2C), we have to perform bitwise logical operations (usually bitwise OR) to abstract informations for decisions.

Yet as far as I know, in the C standard, no spacing or any form of separation between digits is allowed, which makes it more difficult to read the long binary numbers. Using hexadecimal number is somehow worse since it masks the binary number.

Is there a conforming way to express long binary numbers with separators (such as 0b0000 1111 0011 1100) in C?

Share Improve this question edited Feb 3 at 19:57 Eric Postpischil 225k14 gold badges189 silver badges361 bronze badges asked Feb 3 at 17:43 OW1TY2OW1TY2 376 bronze badges 3
  • 1 FWIW, the 0bxxx format was not standard pre-C23 at all. – Eugene Sh. Commented Feb 3 at 17:49
  • 1 C23 introdues, for example, int a = 0b1010'1100'0110'1001;. – Weather Vane Commented Feb 3 at 17:50
  • 2 This is why hex is usually used to express binary numbers. Most experienced programmers know the binary<->hex correspondence. – Barmar Commented Feb 3 at 18:30
Add a comment  | 

4 Answers 4

Reset to default 7

Yes, with the newly released C standard (C23), you can use ' as a digit separator:

uint16_t x = 0b0000'1111'0011'1100;

Digit separators are ignored when determining the value of the constant.

Using a macro you can compose the binary number. You can handle octal numbers specially. Additional casts can be used to handle more bits then unsigned int type. Macro can be overloaded on number of arguments.

The following code allows to create the number with BINARY(0000,1111,0011,1100).

#include <stdint.h>
#include <stdio.h>

// creates a binary 4 digit number from 1010 or 0101 or similar    
#define BINARY_1(a) ( \
    a==  0?  0u:\
    a==  1?  1u:\
    a== 10?  2u:\
    a== 11?  3u:\
    a== 100? 4u:\
    a== 101? 5u:\
    a== 110? 6u:\
    a== 111? 7u:\
    a==1000? 8u:\
    a==1001? 9u:\
    a==1010?10u:\
    a==1011?11u:\
    a==1100?12u:\
    a==1101?13u:\
    a==1110?14u:\
    a==1111?15u:\
    a==0001? 1u:\
    a==0010? 2u:\
    a==0011? 3u:\
    a==0100? 4u:\
    a==0101? 5u:\
    a==0110? 6u:\
    a==0111? 7u:\
    -1)
#define BINARY_2(a,...)   ((uint_least8_t)BINARY_1(a)<<4|BINARY_1(__VA_ARGS__))
#define BINARY_3(a,...)   ((uint_least16_t)BINARY_1(a)<<8|BINARY_2(__VA_ARGS__))
#define BINARY_4(a,...)   ((uint_least16_t)BINARY_1(a)<<12|BINARY_3(__VA_ARGS__))
#define BINARY_5(a,...)   ((uint_least32_t)BINARY_1(a)<<16|BINARY_4(__VA_ARGS__))
#define BINARY_6(a,...)   ((uint_least32_t)BINARY_1(a)<<20|BINARY_5(__VA_ARGS__))
#define BINARY_7(a,...)   ((uint_least32_t)BINARY_1(a)<<24|BINARY_6(__VA_ARGS__))
#define BINARY_8(a,...)   ((uint_least32_t)BINARY_1(a)<<28|BINARY_7(__VA_ARGS__))
#define BINARY_9(a,...)   ((uint_least64_t)BINARY_1(a)<<32|BINARY_8(__VA_ARGS__))
#define BINARY_10(a,...)  ((uint_least64_t)BINARY_1(a)<<36|BINARY_9(__VA_ARGS__))
#define BINARY_11(a,...)  ((uint_least64_t)BINARY_1(a)<<40|BINARY_10(__VA_ARGS__))
#define BINARY_12(a,...)  ((uint_least64_t)BINARY_1(a)<<44|BINARY_11(__VA_ARGS__))
#define BINARY_13(a,...)  ((uint_least64_t)BINARY_1(a)<<48|BINARY_12(__VA_ARGS__))
#define BINARY_14(a,...)  ((uint_least64_t)BINARY_1(a)<<52|BINARY_13(__VA_ARGS__))
#define BINARY_15(a,...)  ((uint_least64_t)BINARY_1(a)<<56|BINARY_14(__VA_ARGS__))
#define BINARY_16(a,...)  ((uint_least64_t)BINARY_1(a)<<60|BINARY_15(__VA_ARGS__))
#define BINARY_17(a,...)  ((__uint128_t)BINARY_1(a)<<64|BINARY_16(__VA_ARGS__))
#define BINARY_18(a,...)  ((__uint128_t)BINARY_1(a)<<68|BINARY_17(__VA_ARGS__))
#define BINARY_19(a,...)  ((__uint128_t)BINARY_1(a)<<72|BINARY_18(__VA_ARGS__))
#define BINARY_20(a,...)  ((__uint128_t)BINARY_1(a)<<76|BINARY_19(__VA_ARGS__))
#define BINARY_21(a,...)  ((__uint128_t)BINARY_1(a)<<80|BINARY_20(__VA_ARGS__))
#define BINARY_22(a,...)  ((__uint128_t)BINARY_1(a)<<84|BINARY_21(__VA_ARGS__))
#define BINARY_23(a,...)  ((__uint128_t)BINARY_1(a)<<88|BINARY_22(__VA_ARGS__))
#define BINARY_24(a,...)  ((__uint128_t)BINARY_1(a)<<92|BINARY_23(__VA_ARGS__))
#define BINARY_25(a,...)  ((__uint128_t)BINARY_1(a)<<96|BINARY_24(__VA_ARGS__))
#define BINARY_26(a,...)  ((__uint128_t)BINARY_1(a)<<100|BINARY_25(__VA_ARGS__))
#define BINARY_27(a,...)  ((__uint128_t)BINARY_1(a)<<104|BINARY_26(__VA_ARGS__))
#define BINARY_28(a,...)  ((__uint128_t)BINARY_1(a)<<108|BINARY_27(__VA_ARGS__))
#define BINARY_29(a,...)  ((__uint128_t)BINARY_1(a)<<112|BINARY_28(__VA_ARGS__))
#define BINARY_30(a,...)  ((__uint128_t)BINARY_1(a)<<116|BINARY_29(__VA_ARGS__))
#define BINARY_31(a,...)  ((__uint128_t)BINARY_1(a)<<120|BINARY_30(__VA_ARGS__))
#define BINARY_32(a,...)  ((__uint128_t)BINARY_1(a)<<124|BINARY_31(__VA_ARGS__))
// macro overloaded on number of argumnets, max 32
#define BINARY_N(\
    _32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1,\
    N,...)  BINARY##N
#define BINARY(...)  BINARY_N(__VA_ARGS__,\
    _32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1,\
    )(__VA_ARGS__)

int main() {
    printf("%B\n", BINARY(0000,1111,0011,1100));
    printf("%B\n", BINARY(1100));
    printf("%B\n", BINARY(0101,1010,100));
    // test 128 bits with gcc extensions - __auto_type and __uint128_t
    const __auto_type bignum = BINARY(1111,1110,1101,1100,1011,1010,1001,1000,0111,0110,0101,0100,0011,0010,0001,0000,1111,1110,1101,1100,1011,1010,1001,1000,0111,0110,0101,0100,0011,0010,0001,0000);
    printf("%llB%064llB\n", (unsigned long long)(bignum >> 64), (unsigned long long)bignum);
    for (int i = 128/4; i; --i) {
        printf("%04B%s", (unsigned)((bignum >> 4 * (i - 1)) & 0xf), i == 1 ? "\n" : ",");
    }
}

Code outputs:

111100111100
1100
10110100100
11111110110111001011101010011000011101100101010000110010000100001111111011011100101110101001100001110110010101000011001000010000
1111,1110,1101,1100,1011,1010,1001,1000,0111,0110,0101,0100,0011,0010,0001,0000,1111,1110,1101,1100,1011,1010,1001,1000,0111,0110,0101,0100,0011,0010,0001,0000

Here is a portable macro (c99) that works for values up to 20 bits and supports blocks of bits of any length. The macro name is Ob with an O like Orange.

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

/* macros to express binary constants using comma separated blocks of bits */
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 202000
// construct binary constant supported by C23 (up to 64 bits)
#define Ob1(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,...) 0b##a##b##c##d##e##f##g##h##i##j##k##l##m##n##o##p##q##r##s##t
#else
// use octal to construct binary constant up to 20 bits
#define Ob4(n)  (int)((((n)>>6)&8)|(((n)>>4)&4)|(((n)>>2)&2)|((n)&1))
#define Ob20(n) ((Ob4((n)>>48)<<16)|(Ob4((n)>>36)<<12)|(Ob4((n)>>24)<<8)|(Ob4((n)>>12)<<4)|Ob4(n))
#define Ob1(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,...) Ob20(0##a##b##c##d##e##f##g##h##i##j##k##l##m##n##o##p##q##r##s##t##LL)
#endif
// Ob takes up to 20 blocks of binary bits
#define Ob(...) Ob1(__VA_ARGS__,,,,,,,,,,,,,,,,,,,)

#define TEST(e)  printf("%32s: %d, %#.4x\n", #e, (int)(e), (int)(e))

int main(void) {
    TEST(strtol("111100111100",NULL,2));
    TEST(Ob(0000,1111,0011,1100));
    TEST(Ob(1111,0011,1100));
    TEST(Ob(111100,11,11,0,0));
    TEST(Ob(1,1,1,1,0,0,1,1,1,1,0,0));
    return 0;
}

Output:

   strtol("111100111100",NULL,2): 3900, 0x0f3c
         Ob(0000,1111,0011,1100): 3900, 0x0f3c
              Ob(1111,0011,1100): 3900, 0x0f3c
            Ob(111100,11,11,0,0): 3900, 0x0f3c
     Ob(1,1,1,1,0,0,1,1,1,1,0,0): 3900, 0x0f3c

Not only binary

The separation issue applies with any lengthy number, be it binary, octal, decimal, hexadecimal. It applies to integers and floating point.

C23 offers the single quote character '.
@Ted Lyngmo

The separator is only used to separate digits and does not affect the value.

Valid sequences include:

uint32_t j = 0b1000'0110'0111'0101'0011'000'1001;
double my_pi = 3.141'592'653'589'793'238'462'643'383'279'5;
uint64_t x = 0x1'23'456'789A'BCDEF;  // Separators need not be evenly spaced.

Invalid sequences include:

'0101     // Separator lacks a prior digit.
0101'     // Separator lacks a trailing digit.
0'b1      // Separator is not directly between binary digits.
1.23'e45  // Separator lacks a trailing digit.
1.'23e45  // Separator lacks a leading digit.
1''23     // Adjacent separators not allowed.

发布评论

评论列表(0)

  1. 暂无评论