7
\$\begingroup\$

I'm writing a library to register the b, B conversion specifiers and make them work the closest possible to o, u, x, X.

Requirements:

  • All "flag characters" should behave as in x, X (or as close as possible, while being meaningful).
  • "Field width" and "precision" should behave as in x, X (or as close as possible, while being meaningful).
  • All "length modifiers" that are valid on o, u, x, X should be valid.
  • They should work on any printf family function (such as fprintf, snprintf, ...).
  • There are some flags that override others in the case of o, u, x, X. Those behaviours should be kept the same.

Basically, they should work so that a user can predict the output from reading the man printf page.

I decided that the ' flag character, which normally should group the output in thousands, would be more meaningful here if it grouped nibbles (half bytes), and instead of using the locale for thousands separator, it should use _. But I'm open to improvements here.

Here goes the code:

/* 2019 - Alejandro Colomar Andrés */
/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include "libalx/base/stdio/printf.h"

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

#include <printf.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* enums ****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* structs / unions *****************************************************
 ******************************************************************************/


/******************************************************************************
 ******* variables ************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* static functions (prototypes) ****************************************
 ******************************************************************************/
static  int printf_output_b     (FILE *stream,
                    const struct printf_info *info,
                    const void *const *args);
static  int printf_arginf_sz_b  (const struct printf_info *info,
                    size_t n, int *argtypes, int *size);


/******************************************************************************
 ******* global functions *****************************************************
 ******************************************************************************/
int alx_printf_init (void)
{

    if (register_printf_specifier('b', printf_output_b, printf_arginf_sz_b))
        return  -1;
    if (register_printf_specifier('B', printf_output_b, printf_arginf_sz_b))
        return  -1;

    return  0;
}


/******************************************************************************
 ******* static functions (definitions) ***************************************
 ******************************************************************************/
static  int printf_output_b     (FILE *stream,
                    const struct printf_info *info,
                    const void *const *args)
{
    uintmax_t   val;
    uintmax_t   utmp;
    bool    bin[sizeof(val) * 8];
    int min_len;
    int bin_len;
    int pad_len;
    int tmp;
    char    pad_ch;
    size_t  len;

    len = 0;

    if (info->is_long_double)
        val = *(unsigned long long *)args[0];
    else if (info->is_long)
        val = *(unsigned long *)args[0];
    else if (info->is_char)
        val = *(unsigned char *)args[0];
    else if (info->is_short)
        val = *(unsigned short *)args[0];
    else
        val = *(unsigned *)args[0];

    /* Binary representation */
    memset(bin, 0, sizeof(bin));
    utmp    = val;
    for (min_len = 0; utmp; min_len++) {
        if (utmp % 2)
            bin[min_len]    = 1;
        utmp >>= 1;
    }
    if (info->prec > min_len)
        bin_len = info->prec;
    else
        bin_len = min_len;

    /* Padding char */
    if ((info->prec != -1)  ||  (info->pad == ' ')  ||  info->left)
        pad_ch = ' ';
    else
        pad_ch = '0';

    /* Padding length */
    if (pad_ch == ' ') {
        pad_len = info->width - bin_len;
        if (info->alt)
            pad_len -= 2;
        if (info->group)
            pad_len -= (bin_len - 1) / 4;
        if (pad_len < 0)
            pad_len = 0;
    }

    /* Padding with ' ' (right aligned) */
    if ((pad_ch == ' ')  &&  !info->left) {
        for (int i = pad_len; i; i--) {
            if (fputc(' ', stream) == EOF)
                return  EOF;
        }
        len += pad_len;
    }

    /* "0b"/"0B" prefix */
    if (info->alt && val) {
        if (fputc('0', stream) == EOF)
            return  EOF;
        if (fputc(info->spec, stream) == EOF)
            return  EOF;
        len += 2;
    }

    /* Padding with '0' */
    if (pad_ch == '0') {
        tmp = info->width - (info->alt * 2);
        if (info->group)
            tmp -= (tmp - min_len + 3) / 4;
        for (int i = tmp - 1; i > min_len - 1; i--) {
            if (fputc('0', stream) == EOF)
                return  EOF;
            if (info->group  &&  !(i % 4)  &&  i) {
                if (fputc('_', stream) == EOF)
                    return  EOF;
                len++;
            }
        }
        len += tmp - min_len;
    }

    /* Print leading zeros to fill precission */
    for (int i = bin_len - 1; i > min_len - 1; i--) {
        if (fputc('0', stream) == EOF)
            return  EOF;
        if (info->group  &&  !(i % 4)  &&  i) {
            if (fputc('_', stream) == EOF)
                return  EOF;
            len++;
        }
    }
    len += bin_len - min_len;

    /* Print number */
    for (int i = min_len - 1; i; i--) {
        if (fputc('0' + bin[i], stream) == EOF)
            return  EOF;
        if (info->group  &&  !(i % 4)  &&  i) {
            if (fputc('_', stream) == EOF)
                return  EOF;
            len++;
        }
    }
    if (fputc('0' + bin[0], stream) == EOF)
        return  EOF;
    len += min_len;

    /* Padding with ' ' (left aligned) */
    if (info->left) {
        for (int i = pad_len; i; i--) {
            if (fputc(' ', stream) == EOF)
                return  EOF;
        }
        len += pad_len;
    }

    return  len;
}

static  int printf_arginf_sz_b  (const struct printf_info *info,
                    size_t n, int *argtypes, int *size)
{

    if (n > 0)
        argtypes[0] = PA_INT;

    return 1;
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

It seems that it works as expected (at least on my tests), but I feel like the code is very heavy and long and could be simplified.

Here is what I've tested:

#include "libalx/base/stdio/printf.h"

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


int main()
{

    alx_printf_init();

    printf("....----....----....----....----\n");
    printf("%llb;\n", 0x5Ellu);
    printf("%lB;\n", 0x5Elu);
    printf("%b;\n", 0x5Eu);
    printf("%hB;\n", 0x5Eu);
    printf("%hhb;\n", 0x5Eu);
    printf("%jb;\n", (uintmax_t)0x5E);
    printf("%zb;\n", (size_t)0x5E);
    printf("....----....----....----....----\n");
    printf("%#b;\n", 0x5Eu);
    printf("%#B;\n", 0x5Eu);
    printf("....----....----....----....----\n");
    printf("%10b;\n", 0x5Eu);
    printf("%010b;\n", 0x5Eu);
    printf("%.10b;\n", 0x5Eu);
    printf("....----....----....----....----\n");
    printf("%-10B;\n", 0x5Eu);
    printf("....----....----....----....----\n");
    printf("%'B;\n", 0x5Eu);
    printf("....----....----....----....----\n");   
    printf("....----....----....----....----\n");
    printf("%#16.12b;\n", 0xAB);
    printf("%-#'20.12b;\n", 0xAB);
    printf("%#'020B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%#020B;\n", 0xAB);
    printf("%'020B;\n", 0xAB);
    printf("%020B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%#021B;\n", 0xAB);
    printf("%'021B;\n", 0xAB);
    printf("%021B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%#022B;\n", 0xAB);
    printf("%'022B;\n", 0xAB);
    printf("%022B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%#023B;\n", 0xAB);
    printf("%'023B;\n", 0xAB);
    printf("%023B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%-#'19.11b;\n", 0xAB);
    printf("%#'019B;\n", 0xAB);
    printf("%#019B;\n", 0xAB);
    printf("....----....----....----....----\n");
    printf("%'019B;\n", 0xAB);
    printf("%019B;\n", 0xAB);
    printf("%#016b;\n", 0xAB);
    printf("....----....----....----....----\n");

    return 0;
}

And its output:

....----....----....----....----                                                  
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
1011110;                                                                          
....----....----....----....----                                                  
0b1011110;                                                                        
0B1011110;                                                                        
....----....----....----....----                                                  
   1011110;                                                                       
0001011110;                                                                       
0001011110;                                                                       
....----....----....----....----                                                  
1011110   ;                                                                       
....----....----....----....----                                                  
101_1110;                                                                         
....----....----....----....----
....----....----....----....----                                                  
  0b000010101011;                                                                 
0b0000_1010_1011    ;                                                             
0B000_0000_1010_1011;                                                             
....----....----....----....----                                                  
0B000000000010101011;                                                             
0_0000_0000_1010_1011;                                                            
00000000000010101011;                                                             
....----....----....----....----                                                  
0B0000000000010101011;                                                            
0_0000_0000_1010_1011;                                                            
000000000000010101011;                                                            
....----....----....----....----                                                  
0B00000000000010101011;                                                           
00_0000_0000_1010_1011;                                                           
0000000000000010101011;                                                           
....----....----....----....----                                                  
0B000000000000010101011;                                                          
000_0000_0000_1010_1011;                                                          
00000000000000010101011;                                                          
....----....----....----....----                                                  
0b000_1010_1011    ;                                                              
0B00_0000_1010_1011;                                                              
0B00000000010101011;                                                              
....----....----....----....----                                                  
0000_0000_1010_1011;                                                              
0000000000010101011;                                                              
0b00000010101011;                                                                 
....----....----....----....----

I understood the use of printf_output_b, but I still don't know very well the use of printf_arginf_sz_b, and if I'm missing anything.

Is there anything you miss here, or you think could be improved?

Also, let's say that uintmax_t or size_t are wider than unsigned long long. How would I handle that? I don't receive any information about those in the struct, AFAIK.

EDIT: Add the struct and enum definitions from <printf.h>

struct printf_info{
 int prec;          /* Precision. */
 int width;         /* Width. */
 wchar_t spec;          /* Format letter. */
 unsigned int is_long_double:1;/* L flag. */
 unsigned int is_short:1;   /* h flag. */
 unsigned int is_long:1;    /* l flag. */
 unsigned int alt:1;        /* # flag. */
 unsigned int space:1;      /* Space flag. */
 unsigned int left:1;       /* - flag. */
 unsigned int showsign:1;   /* + flag. */
 unsigned int group:1;      /* ' flag. */
 unsigned int extra:1;      /* For special use. */
 unsigned int is_char:1;    /* hh flag. */
 unsigned int wide:1;       /* Nonzero for wide character streams. */
 unsigned int i18n:1;       /* I flag. */
 unsigned int is_binary128:1;   /* Floating-point argument is ABI-compatible                 with IEC 60559 binary128. */
 unsigned int __pad:3;      /* Unused so far. */
 unsigned short int user;   /* Bits for user-installed modifiers. */
 wchar_t pad;           /* Padding character. */
};

enum{               /* C type: */
 PA_INT,            /* int */
 PA_CHAR,           /* int, cast to char */
 PA_WCHAR,          /* wide char */
 PA_STRING,         /* const char *, a '\0'-terminated string */
 PA_WSTRING,            /* const wchar_t *, wide character string */
 PA_POINTER,            /* void * */
 PA_FLOAT,          /* float */
 PA_DOUBLE,         /* double */
 PA_LAST
};
```
\$\endgroup\$
  • \$\begingroup\$ What exactly are you asking, do you want us to explain what printf_arginf_sz_b() does as well as the size of uintmax_t and size_t? \$\endgroup\$ – pacmaninbw May 9 at 15:58
  • \$\begingroup\$ @pacmaninbw I'd like to see improvements in code cleanness/performance and if I anticipated (as per the requirements) all possible combinations of flags (including "%jb" (uint_max) for example). Also, obviously, if there is any UB, I would like to know. \$\endgroup\$ – Cacahuete Frito May 9 at 16:12
  • \$\begingroup\$ Could you please add the file(s) that define struct printf_info and PA_INT. \$\endgroup\$ – pacmaninbw May 9 at 16:34
  • 1
    \$\begingroup\$ @pacmaninbw They're all from <printf.h> from glibc (github.com/lattera/glibc/blob/master/stdio-common/printf.h). I edited the question to add them. \$\endgroup\$ – Cacahuete Frito May 9 at 20:00
3
\$\begingroup\$

zero

min_len is calculated as 0 when val == 0. I'd expect min_len to be 1.

Casual conversion

Pedantic: code returns size_t len as int with no range check in the conversion. Might as well just use int len.

len calculation

Code does not display the return value of printf() in its tests. I have suspicions about its correctness. Suggest instead to simply pair each fputc() with a len++.

\$\endgroup\$
3
\$\begingroup\$

EDIT: I also fixed a typo: main should be int main(void)!


After fixing the bugs @chux found, there was still another bug:

This line:

        tmp -= (tmp - min_len + 3) / 4;

should be:

        tmp -= tmp / 5 - !(tmp % 5);

I also restructured the big function into smaller functions, and used CHAR_BIT instead of the magic number 8:

/* 2019 - Alejandro Colomar Andrés */
/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include "libalx/base/stdio/printf.h"

#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <printf.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/
#define BIN_REPR_BUFSIZ (sizeof(uintmax_t) * CHAR_BIT)


/******************************************************************************
 ******* enums ****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* structs / unions *****************************************************
 ******************************************************************************/
struct  Printf_Pad {
        char    ch;
        int     len;
};


/******************************************************************************
 ******* variables ************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* static functions (prototypes) ****************************************
 ******************************************************************************/
static  int     printf_b_output         (FILE *stream,
                                        const struct printf_info *info,
                                        const void *const args[]);
static  int     printf_b_arginf_sz      (const struct printf_info *info,
                                        size_t n, int *argtypes, int *size);

static  uintmax_t printf_b_value        (const struct printf_info *info,
                                        const void *arg);
static  int     printf_b_bin_repr       (bool bin[BIN_REPR_BUFSIZ],
                                        const struct printf_info *info,
                                        const void *arg);
static  int     printf_b_bin_len        (const struct printf_info *info,
                                        int min_len);
static  int     printf_b_pad_len        (const struct printf_info *info,
                                        int bin_len);
static  int     printf_b_print_prefix   (FILE *stream,
                                        const struct printf_info *info);
static  int     printf_b_pad_zeros      (FILE *stream,
                                        const struct printf_info *info,
                                        int min_len);
static  int     printf_b_print_number   (FILE *stream,
                                        const struct printf_info *info,
                                        bool bin[BIN_REPR_BUFSIZ],
                                        int min_len, int bin_len);
static  char    printf_pad_ch           (const struct printf_info *info);
static  int     printf_pad_spaces       (FILE *stream, int pad_len);




/******************************************************************************
 ******* global functions *****************************************************
 ******************************************************************************/
int     alx_printf_init (void)
{

        if (register_printf_specifier('b', printf_b_output, printf_b_arginf_sz))
                return  -1;
        if (register_printf_specifier('B', printf_b_output, printf_b_arginf_sz))
                return  -1;

        return  0;
}


/******************************************************************************
 ******* static functions (definitions) ***************************************
 ******************************************************************************/
static  int     printf_b_output         (FILE *stream,
                                        const struct printf_info *info,
                                        const void *const args[])
{
        struct  Printf_Pad  pad = {0};
        bool    bin[BIN_REPR_BUFSIZ];
        int     min_len;
        int     bin_len;
        int     len;
        int     tmp;

        len = 0;

        min_len = printf_b_bin_repr(bin, info, args[0]);
        bin_len = printf_b_bin_len(info, min_len);

        pad.ch = printf_pad_ch(info);
        if (pad.ch == ' ')
                pad.len = printf_b_pad_len(info, bin_len);

        /* Padding with ' ' (right aligned) */
        if ((pad.ch == ' ')  &&  !info->left) {
                tmp = printf_pad_spaces(stream, pad.len);
                if (tmp == EOF)
                        return  EOF;
                len += tmp;
        }

        /* "0b"/"0B" prefix */
        if (info->alt) {
                tmp = printf_b_print_prefix(stream, info);
                if (tmp == EOF)
                        return  EOF;
                len += tmp;
        }

        /* Padding with '0' */
        if (pad.ch == '0') {
                tmp = printf_b_pad_zeros(stream, info, min_len);
                if (tmp == EOF)
                        return  EOF;
                len += tmp;
        }

        /* Print number (including leading 0s to fill precission) */
        tmp = printf_b_print_number(stream, info, bin, min_len, bin_len);
        if (tmp == EOF)
                return  EOF;
        len += tmp;

        /* Padding with ' ' (left aligned) */
        if (info->left) {
                tmp = printf_pad_spaces(stream, pad.len);
                if (tmp == EOF)
                        return  EOF;
                len += tmp;
        }

        return  len;
}

static  int     printf_b_arginf_sz      (const struct printf_info *info,
                                        size_t n, int *argtypes, int *size)
{

        (void)info;
        (void)size;

        if (n > 0)
                argtypes[0] = PA_INT;

        return 1;
}

static  uintmax_t printf_b_value        (const struct printf_info *info,
                                        const void *arg)
{

        if (info->is_long_double)
                return  *(unsigned long long *)arg;
        if (info->is_long)
                return  *(unsigned long *)arg;
        if (info->is_char)
                return  *(unsigned char *)arg;
        if (info->is_short)
                return  *(unsigned short *)arg;
        return  *(unsigned *)arg;
}

static  int     printf_b_bin_repr       (bool bin[BIN_REPR_BUFSIZ],
                                        const struct printf_info *info,
                                        const void *arg)
{
        uintmax_t   val;
        int         min_len;

        val = printf_b_value(info, arg);

        memset(bin, 0, sizeof(bin[0]) * BIN_REPR_BUFSIZ);
        for (min_len = 0; val; min_len++) {
                if (val % 2)
                        bin[min_len]    = 1;
                val >>= 1;
        }

        if (!min_len)
                return  1;
        return  min_len;
}

static  int     printf_b_bin_len        (const struct printf_info *info,
                                        int min_len)
{

        if (info->prec > min_len)
                return  info->prec;
        return  min_len;
}

static  int     printf_b_pad_len        (const struct printf_info *info,
                                        int bin_len)
{
        int pad_len;

        pad_len = info->width - bin_len;
        if (info->alt)
                pad_len -= 2;
        if (info->group)
                pad_len -= (bin_len - 1) / 4;
        if (pad_len < 0)
                pad_len = 0;

        return  pad_len;
}

static  int     printf_b_print_prefix   (FILE *stream,
                                        const struct printf_info *info)
{
        int len;

        len = 0;
        if (fputc('0', stream) == EOF)
                return  EOF;
        len++;
        if (fputc(info->spec, stream) == EOF)
                return  EOF;
        len++;

        return  len;
}

static  int     printf_b_pad_zeros      (FILE *stream,
                                        const struct printf_info *info,
                                        int min_len)
{
        int len;
        int tmp;

        len = 0;
        tmp = info->width - (info->alt * 2);
        if (info->group)
                tmp -= tmp / 5 - !(tmp % 5);
        for (int i = tmp - 1; i > min_len - 1; i--) {
                if (fputc('0', stream) == EOF)
                        return  EOF;
                len++;
                if (info->group  &&  !(i % 4)) {
                        if (fputc('_', stream) == EOF)
                                return  EOF;
                        len++;
                }
        }

        return  len;
}

static  int     printf_b_print_number   (FILE *stream,
                                        const struct printf_info *info,
                                        bool bin[sizeof(uintmax_t) * CHAR_BIT],
                                        int min_len, int bin_len)
{
        int len;

        len = 0;

        /* Print leading zeros to fill precission */
        for (int i = bin_len - 1; i > min_len - 1; i--) {
                if (fputc('0', stream) == EOF)
                        return  EOF;
                len++;
                if (info->group  &&  !(i % 4)) {
                        if (fputc('_', stream) == EOF)
                                return  EOF;
                        len++;
                }
        }

        /* Print number */
        for (int i = min_len - 1; i; i--) {
                if (fputc('0' + bin[i], stream) == EOF)
                        return  EOF;
                len++;
                if (info->group  &&  !(i % 4)) {
                        if (fputc('_', stream) == EOF)
                                return  EOF;
                        len++;
                }
        }
        if (fputc('0' + bin[0], stream) == EOF)
                return  EOF;
        len++;

        return  len;
}

static  char    printf_pad_ch           (const struct printf_info *info)
{

        if ((info->prec != -1)  ||  (info->pad == ' ')  ||  info->left)
                return  ' ';
        return  '0';
}

static  int     printf_pad_spaces       (FILE *stream, int pad_len)
{
        int len;

        len = 0;
        for (int i = pad_len; i; i--) {
                if (fputc(' ', stream) == EOF)
                        return  EOF;
                len++;
        }

        return  len;
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

I also added some more tests to be able to detect that bug, which I was suspecting that existed; now I also show the value of len (calculated indirectly from the return value of printf):

/* Test */
#include "libalx/base/stdio/printf.h"


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


int main(void)
{
    int len;
    char buff[BUFSIZ];

    alx_printf_init();

    snprintf(buff, 30, "Hey, %i == %#b :)\n", 5, 5);
    printf("%s", buff);
    printf("\n");

    printf("....----....----....----....----\n");
    len = printf("%llb;\n", 0x5Ellu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%lB;\n", 0x5Elu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%hB;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%hhb;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%jb;\n", (uintmax_t)0x5E);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%zb;\n", (size_t)0x5E);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#B;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%10b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%010b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%.10b;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%-10B;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'B;\n", 0x5Eu);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");   
    printf("....----....----....----....----\n");
    len = printf("%#16.12b;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%-#'20.12b;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'020B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#020B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'020B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%020B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#021B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'021B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%021B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#022B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'022B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%022B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%#023B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'023B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%023B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%-#'19.11b;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'019B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#019B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'019B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%019B;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#016b;\n", 0xAB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'010B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'010B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'010B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'010B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0xB);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0x1B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0x2B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0x4B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");
    len = printf("%'010B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'010B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'011B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'011B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'012B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'012B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%'013B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    len = printf("%#'013B;\n", 0x8B);
    printf("%i\n", len - strlen(";\n"));
    printf("....----....----....----....----\n");

    return 0;
}

Which shows the following output:

Hey, 5 == 0b101 :)

....----....----....----....----
1011110;
7
1011110;
7
1011110;
7
1011110;
7
1011110;
7
1011110;
7
1011110;
7
....----....----....----....----
0b1011110;
9
0B1011110;
9
....----....----....----....----
   1011110;
10
0001011110;
10
0001011110;
10
....----....----....----....----
1011110   ;
10
....----....----....----....----
101_1110;
8
....----....----....----....----
....----....----....----....----
  0b000010101011;
16
0b0000_1010_1011    ;
20
0B000_0000_1010_1011;
20
....----....----....----....----
0B000000000010101011;
20
0_0000_0000_1010_1011;
21
00000000000010101011;
20
....----....----....----....----
0B0000000000010101011;
21
0_0000_0000_1010_1011;
21
000000000000010101011;
21
....----....----....----....----
0B00000000000010101011;
22
00_0000_0000_1010_1011;
22
0000000000000010101011;
22
....----....----....----....----
0B000000000000010101011;
23
000_0000_0000_1010_1011;
23
00000000000000010101011;
23
....----....----....----....----
0b000_1010_1011    ;
19
0B00_0000_1010_1011;
19
0B00000000010101011;
19
....----....----....----....----
0000_0000_1010_1011;
19
0000000000010101011;
19
0b00000010101011;
16
....----....----....----....----
....----....----....----....----
0_0000_1011;
11
0B000_1011;
10
0_0001_1011;
11
0B001_1011;
10
0_0010_1011;
11
0B010_1011;
10
0_0100_1011;
11
0B100_1011;
10
0_1000_1011;
11
0B1000_1011;
11
....----....----....----....----
0_0000_1011;
11
0B000_1011;
10
0_0000_1011;
11
0B0000_1011;
11
00_0000_1011;
12
0B0_0000_1011;
13
000_0000_1011;
13
0B0_0000_1011;
13
....----....----....----....----
0_0001_1011;
11
0B001_1011;
10
0_0001_1011;
11
0B0001_1011;
11
00_0001_1011;
12
0B0_0001_1011;
13
000_0001_1011;
13
0B0_0001_1011;
13
....----....----....----....----
0_0010_1011;
11
0B010_1011;
10
0_0010_1011;
11
0B0010_1011;
11
00_0010_1011;
12
0B0_0010_1011;
13
000_0010_1011;
13
0B0_0010_1011;
13
....----....----....----....----
0_0100_1011;
11
0B100_1011;
10
0_0100_1011;
11
0B0100_1011;
11
00_0100_1011;
12
0B0_0100_1011;
13
000_0100_1011;
13
0B0_0100_1011;
13
....----....----....----....----
0_1000_1011;
11
0B1000_1011;
11
0_1000_1011;
11
0B1000_1011;
11
00_1000_1011;
12
0B0_1000_1011;
13
000_1000_1011;
13
0B0_1000_1011;
13
....----....----....----....----
````
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.