Binary Notation in C23

While the C language offers hexadecimal notation and output, it ignores binary. Now with the C 23 standard, binary expression and output is supported.

If you were to guess, what do you think would be the prefix for an integer expressed in binary notation?

Yep, it’s 0b. Simple. Consistent.

The value 26 expressed in binary for the C23 standard looks like this: 0b11010

Here is sample code for C23 that outputs a binary integer value in decimal and hex:

2023_12_30-Lesson-a.c

#include <stdio.h>

int main()
{
    int a = 0b11010;

    printf("Decimal: %d\n",a);
    printf("Hex: %x\n",a);

    return 0;
}

Remember from last week’s Lesson, you must specify the -std=c2x switch for the compiler to recognize and use the C23 standard. When active, the switch enables the compiler to recognize the 0b11010 notation and value. Here is the output:

Decimal: 26
Hex: 1a

The C23 flavor has another binary trick up its sleeve: the tick binary separator. The tick separator isn’t a new C feature. It’s available now with hexadecimal values. With binary notation, however, the tick separator becomes highly useful.

Programmers often write binary values in clumps of four. These match up with the hex digits 0 through F:

Binary Hex Binary Hex Binary Hex Binary Hex
0000 0 0100 4 1000 8 1100 c
0001 1 0101 5 1001 9 1101 d
0010 2 0110 6 1010 a 1110 e
0011 3 0111 7 1011 b 1111 f

I’ve written about these tick separators before, specifically how they’re used in hexadecimal values. Here’s how they’re useful in binary notation:

int a = 0b001'1010;

The ticks need not be used every four positions, either:

int a = 0b0'01'10'10;

Or even:

int a = 0b00'11010;

How you place the ticks is up to you, but they’re used to separate a long binary value for easy reading.

The C23 standard also defines the %b conversion specification, which is used as a placeholder in a printf() or scanf() format string to output or accept binary values. So the following statement could be added to the code:

printf("Binary: %b\n",a);

For some reason, this conversion specification throws a warning when clang-15 builds the program. Even so, a program is created:

Decimal: 26
Hex: 1a
Binary: 11010

I tested the %b placeholder with the following code as well:

2023_12_30-Lesson-b.c

#include <stdio.h>

int main()
{
    int b = 26;

    printf("%b\n",b);

    return 0;
}

Again, the warning is generated, but the program produces the proper output:

11010

Feeling saucy, I decided to see whether a width specifier works with the %b placeholder. I changed the program’s printf() statement to read %8b. Here’s the output:

   11010

Yes, the output is 8-characters wide, but I wanted leading zeros. So I updated the code to use %08b instead. Here’s the result:

00011010

Pretty cool, especially to this nerd.

The %b placeholder does not work in the scanf() function for reading binary input. I figure this feature isn’t implemented for the clang-15 compiler I’m using. My guess and hope is that it finds its way into a future release of clang.

In next week’s Lesson I cover a new C23 data type, precise integers. Happy New Year, everyone!

7 thoughts on “Binary Notation in C23

  1. Treating C++ as “C with classes” it has been possible to use “binary literals” (N3472) as well as “single quote (‘) as a digit separator” (N3781) since g++ 4.9 (or, on Windows, Visual Studio 2015):

    #include <stdio.h>

    /* Misuse C++ as a glorified C compiler: */
    int main (void)
    {
    #if __cpp_binary_literals  /* C++(20) feature test macro for binary literals */
      printf (“bin: %d\n”, 0b1111’1111);
    #endif

    #if __cplusplus >= 201402  /* C++14 or later? */
      printf (“hex: %d\n”, 0xDE’AD’BE’AF);
    #endif

      return (0);
    }

    [__linux__]: g++ -std=c++14 -Wall -o digitsep digitsep.c
    [_Win32]: cl /TP /std:c++14 /Zc:__cplusplus /EHsc digitsep.c

    That notwithstanding, it is a good thing those features are now coming to C(23) as well… seeing as, especially on embedded systems/when working with microcontrollers, one does not always have the luxury of working with fully-fledged C++ compilers.

    Anyway: Happy New Year to all “C” aficionados reading this blog!

  2. Although I only do so somewhat irregularly: my pleasure ☺

    Youʼre doing a great service to the community with these regular blog entries, insights and challenges!

  3. I hope C23 prints negative numbers in binary correctly instead of just a minus sign followed by the bits for the equivalent positive number which is what Python does.

    Where you use %08b can you somehow use a variable instead? Specifically I was thinking sizeof(myvariable) * 8 might be useful.

  4. @ChrisWebb: Where you use %08b can you somehow use a variable instead?

    This should be doable with the “minimum field width specifier” of the printf() format string—as per section §7.9.6.1 of the ISO/IEC 9899:1990 standard: “The field width takes the form of an asterisk * or a decimal integer.”

    #include <stdio.h>
    #include <limits.h>

    int main (void)
    { int value = 123456789;

      printf (“bin: %0*b\n”, (int)(CHAR_BIT*sizeof(value)), value);
      value = -2;
      printf (“neg: %b\n”, value);
    }

    Compiling the above with ‘-std=c2x’, the programs output is as follows:

    bin: 00000111010110111100110100010101
    neg: 11111111111111111111111111111110

    Where Python err’s, C23 does seem to get it right ☺

  5. No problem. I have to admit: in regular code/quick nʼ dirty functions I too often just use 8*<byte count>&semi; especially in library functions CHAR_BIT does give off a more polished impression, however.

Leave a Reply