Existe-t-il un convertisseur printf pour imprimer au format binaire?


435

Je peux imprimer avec printf comme un nombre hexadécimal ou octal. Existe-t-il une balise de format à imprimer en tant que base binaire ou arbitraire?

J'utilise gcc.

printf("%d %x %o\n", 10, 10, 10); //prints "10 A 12\n"
print("%b\n", 10); // prints "%b\n"

Pour autant que je sache, vous ne pouvez pas faire cela en utilisant printf. Vous pouvez, bien sûr, écrire une méthode d'aide pour y parvenir, mais cela ne ressemble pas à la direction que vous souhaitez suivre.
Ian P

Il n'y a pas de format prédéfini pour cela. Vous devez le transformer vous-même en chaîne, puis imprimer la chaîne.
rslite

Une recherche rapide sur Google a produit cette page avec des informations qui peuvent être utiles: forums.macrumors.com/archive/index.php/t-165959.html
Ian P

12
Pas dans le cadre de la bibliothèque ANSI Standard C - si vous écrivez du code portable, la méthode la plus sûre est de rouler le vôtre.
tomlogic

Une solution standard et générique (pour tout type intégral de n'importe quelle longueur) de la conversion en chaîne binaire en C ++: stackoverflow.com/a/31660310/1814353
luart

Réponses:


267

Hacky mais fonctionne pour moi:

#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte)  \
  (byte & 0x80 ? '1' : '0'), \
  (byte & 0x40 ? '1' : '0'), \
  (byte & 0x20 ? '1' : '0'), \
  (byte & 0x10 ? '1' : '0'), \
  (byte & 0x08 ? '1' : '0'), \
  (byte & 0x04 ? '1' : '0'), \
  (byte & 0x02 ? '1' : '0'), \
  (byte & 0x01 ? '1' : '0') 
printf("Leading text "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(byte));

Pour les types multi-octets

printf("m: "BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN"\n",
  BYTE_TO_BINARY(m>>8), BYTE_TO_BINARY(m));

Vous avez malheureusement besoin de toutes les citations supplémentaires. Cette approche présente les risques d'efficacité des macros (ne passez pas de fonction comme argument BYTE_TO_BINARY), mais évite les problèmes de mémoire et les invocations multiples de strcat dans certaines des autres propositions ici.


13
Et a également l'avantage d'être invocable plusieurs fois dans printflequel ceux avec des statictampons ne peuvent pas.
Patrick Schlüter du

4
J'ai pris la liberté de changer le %den %c, car il devrait être encore plus rapide ( %ddoit effectuer une conversion digit-> char, tout en %c

3
Publié une version étendue de cette macro avec prise en charge int 16, 32, 64 bits: stackoverflow.com/a/25108449/432509
ideasman42

2
Notez que cette approche n'est pas compatible avec les piles. En supposant que intle système est 32 bits, l'impression d'une seule valeur 32 bits nécessitera de l'espace pour les valeurs 32 * 4 octets; 128 octets au total. Ce qui, selon la taille de la pile, peut ou non être un problème.
user694733

1
il est important d'ajouter des parenthèses autour de l'octet dans la macro ou vous pourriez rencontrer des problèmes lors de l'envoi d'une opération BYTE_TO_BINARY (a | b) -> a | b & 0x01! = (a | b) & 0x01
Ivan Hoffmann

203

Imprimer le binaire pour tout type de données

//assumes little endian
void printBits(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char*) ptr;
    unsigned char byte;
    int i, j;

    for (i=size-1;i>=0;i--)
    {
        for (j=7;j>=0;j--)
        {
            byte = (b[i] >> j) & 1;
            printf("%u", byte);
        }
    }
    puts("");
}

tester

int main(int argv, char* argc[])
{
        int i = 23;
        uint ui = UINT_MAX;
        float f = 23.45f;
        printBits(sizeof(i), &i);
        printBits(sizeof(ui), &ui);
        printBits(sizeof(f), &f);
        return 0;
}

8
Suggère size_t i; for (i=size; i-- > 0; )d'éviter size_tou de intmésappariement.
chux

1
Quelqu'un pourrait-il expliquer la logique de ce code?
jII

2
Prenez chaque octet ptr(boucle externe); puis pour chaque bit l'octet courant (boucle interne), masquez l'octet par le bit courant ( 1 << j). Décalez ce droit pour obtenir un octet contenant 0 ( 0000 0000b) ou 1 ( 0000 0001b). Imprimez l'octet résultant printf avec le format %u. HTH.
nielsbot

1
@ ZX9 Notez que le code suggéré utilisé >avec size_tet non le >=de votre commentaire pour déterminer quand terminer la boucle.
chux

3
@ ZX9 Encore un commentaire original utile de votre part car les codeurs doivent être prudents compte tenu de l'utilisation du cas de bord de >et >=avec les types non signés. 0est un cas de bord non signé et se produit généralement, contrairement aux mathématiques signées avec moins courantes INT_MAX/INT_MIN.
chux

151

Voici un hack rapide pour démontrer les techniques pour faire ce que vous voulez.

#include <stdio.h>      /* printf */
#include <string.h>     /* strcat */
#include <stdlib.h>     /* strtol */

const char *byte_to_binary
(
    int x
)
{
    static char b[9];
    b[0] = '\0';

    int z;
    for (z = 128; z > 0; z >>= 1)
    {
        strcat(b, ((x & z) == z) ? "1" : "0");
    }

    return b;
}

int main
(
    void
)
{
    {
        /* binary string to int */

        char *tmp;
        char *b = "0101";

        printf("%d\n", strtol(b, &tmp, 2));
    }

    {
        /* byte to binary string */

        printf("%s\n", byte_to_binary(5));
    }

    return 0;
}

2
C'est certainement moins "bizarre" que l'écriture personnalisée d'une surcharge d'échappement pour printf. C'est aussi simple à comprendre pour un développeur nouveau dans le code.
Furious Coder

43
Quelques changements: strcatest une méthode inefficace pour ajouter un seul caractère à la chaîne à chaque passage de la boucle. Ajoutez plutôt a char *p = b;et remplacez la boucle intérieure par *p++ = (x & z) ? '1' : '0'. zdevrait commencer à 128 (2 ^ 7) au lieu de 256 (2 ^ 8). Pensez à mettre à jour pour prendre un pointeur sur le tampon à utiliser (pour la sécurité des threads), similaire à inet_ntoa().
tomlogic

3
@EvilTeach: Vous utilisez vous-même un opérateur ternaire comme paramètre strcat()! Je suis d'accord que strcatc'est probablement plus facile à comprendre que la post-incrémentation d'un pointeur déréférencé pour l'affectation, mais même les débutants doivent savoir comment utiliser correctement la bibliothèque standard. Peut-être que l'utilisation d'un tableau indexé pour l'affectation aurait été une bonne démonstration (et fonctionnera en fait, car elle bn'est pas réinitialisée à tous les zéros chaque fois que vous appelez la fonction).
tomlogic

3
Aléatoire: le caractère du tampon binaire est statique et est effacé de tous les zéros de l'affectation. Cela ne l'effacera que la première fois qu'il sera exécuté, et après cela, il ne sera pas effacé, mais utilisera plutôt la dernière valeur.
markwatson

8
En outre, cela devrait Prouver que le résultat précédent sera invalide après avoir appelé à nouveau la fonction, afin que les appelants ne doit pas essayer de l' utiliser comme ceci: printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann

84

Il n'y a pas de spécificateur de conversion binaire dans la glibc normalement.

Il est possible d'ajouter des types de conversion personnalisés à la famille de fonctions printf () dans la glibc. Voir register_printf_function pour plus de détails. Vous pouvez ajouter une conversion% b personnalisée pour votre propre usage, si cela simplifie le code d'application pour le rendre disponible.

Voici un exemple de mise en œuvre d'un format d'impression personnalisé dans la glibc.


J'ai toujours écrit mon propre v [snf] printf () pour les cas limités où je voulais différents radix: si heureux d'avoir parcouru cela.
Jamie

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Il y a une nouvelle fonction pour faire la même chose, cependant: register_printf_specifier(). Un exemple de la nouvelle utilisation peut être trouvé ici: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito

47

Vous pouvez utiliser une petite table pour améliorer la vitesse 1 . Des techniques similaires sont utiles dans le monde embarqué, par exemple, pour inverser un octet:

const char *bit_rep[16] = {
    [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011",
    [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111",
    [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011",
    [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111",
};

void print_byte(uint8_t byte)
{
    printf("%s%s", bit_rep[byte >> 4], bit_rep[byte & 0x0F]);
}

1 Je me réfère principalement aux applications intégrées où les optimiseurs ne sont pas si agressifs et où la différence de vitesse est visible.


27

Imprimez le bit le moins significatif et déplacez-le vers la droite. Faire cela jusqu'à ce que l'entier devienne zéro imprime la représentation binaire sans zéros de tête mais dans l'ordre inverse. En utilisant la récursivité, l'ordre peut être corrigé assez facilement.

#include <stdio.h>

void print_binary(int number)
{
    if (number) {
        print_binary(number >> 1);
        putc((number & 1) ? '1' : '0', stdout);
    }
}

Pour moi, c'est l'une des solutions les plus propres au problème. Si vous aimez le 0bpréfixe et un nouveau caractère de ligne de fin, je vous suggère d'habiller la fonction.

Démo en ligne


erreur: trop peu d'arguments pour appeler la fonction, attendus 2, ont 1 putc ((nombre & 1)? '1': '0');
Koray Tugay

@KorayTugay Merci d'avoir signalé cela. J'ai corrigé l'appel de fonction et ajouté une démo.
danijar

6
vous devez également utiliser un nombre entier non signé, car lorsque le nombre donné est négatif, la fonction entre dans un appel récursif sans fin.
Puffy

Approche plus efficace, car en ASCII, '0' + 1 = '1':putc('0'+(number&1), stdout);
Roger Dueck

22

Sur la base de la réponse de @William Whyte, ceci est une macro qui fournit int8, 16, 32et 64versions, réutilisant les INT8macro pour éviter la répétition.

/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16             PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32             PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

#include <stdio.h>
int main() {
    long long int flag = 1648646756487983144ll;
    printf("My Flag "
           PRINTF_BINARY_PATTERN_INT64 "\n",
           PRINTF_BYTE_TO_BINARY_INT64(flag));
    return 0;
}

Cela produit:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Pour plus de lisibilité, vous pouvez ajouter un séparateur, par exemple:

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000

C'est excellent. Y a-t-il une raison particulière pour imprimer les bits commençant par les bits les moins significatifs?
gaganso

2
comment recommanderiez-vous d'ajouter la virgule?
nmz787

Ajouterait une version groupée de PRINTF_BYTE_TO_BINARY_INT#define à utiliser éventuellement.
ideasman42

16

Voici une version de la fonction qui ne souffre pas de problèmes de réentrance ou de limites de taille / type d'argument:

#define FMT_BUF_SIZE (CHAR_BIT*sizeof(uintmax_t)+1)
char *binary_fmt(uintmax_t x, char buf[static FMT_BUF_SIZE])
{
    char *s = buf + FMT_BUF_SIZE;
    *--s = 0;
    if (!x) *--s = '0';
    for(; x; x/=2) *--s = '0' + x%2;
    return s;
}

Notez que ce code fonctionnerait aussi bien pour n'importe quelle base entre 2 et 10 si vous remplacez simplement les 2 par la base souhaitée. L'utilisation est:

char tmp[FMT_BUF_SIZE];
printf("%s\n", binary_fmt(x, tmp));

xest toute expression intégrale.


7
Oui, tu peux le faire. Mais c'est vraiment un mauvais design. Même si vous n'avez pas de threads ou de réentrance, l'appelant doit être conscient que le tampon statique est réutilisé et que des choses comme char *a = binary_fmt(x), *b = binary_fmt(y);cela ne fonctionneront pas comme prévu. Forcer l'appelant à passer un tampon rend explicites les exigences de stockage; l'appelant est bien sûr libre d'utiliser un tampon statique si cela est vraiment souhaité, puis la réutilisation du même tampon devient explicite. Notez également que, sur les PIC ABI modernes, les tampons statiques coûtent généralement plus de code d'accès que les tampons de la pile.
R .. GitHub STOP HELPING ICE

8
C'est toujours une mauvaise conception. Cela nécessite une étape de copie supplémentaire dans ces cas, et ce n'est pas moins cher que de demander à l'appelant de fournir le tampon même dans les cas où la copie ne serait pas requise. L'utilisation du stockage statique n'est qu'un mauvais idiome.
R .. GitHub STOP HELPING ICE

3
Devoir polluer l'espace de noms du préprocesseur ou de la table des symboles de variables avec un nom supplémentaire inutile qui doit être utilisé pour dimensionner correctement le stockage qui doit être alloué par chaque appelant, et forcer chaque appelant à connaître cette valeur et à allouer la quantité nécessaire de stockage, est une mauvaise conception lorsque la solution de stockage local de fonction la plus simple suffira pour la plupart des buts et objectifs, et lorsqu'un simple appel strdup () couvre 99% du reste des utilisations.
Greg A. Woods

5
Ici, nous allons devoir être en désaccord. Je ne vois pas comment l'ajout d'un symbole de préprocesseur discret se rapproche de la nocivité de limiter sévèrement les cas d'utilisation, de rendre l'interface sujette aux erreurs, de réserver un stockage permanent pendant la durée du programme pour une valeur temporaire et de générer un code pire sur la plupart plates-formes modernes.
R .. GitHub STOP HELPING ICE

5
Je ne préconise pas la micro-optimisation sans raison (c'est-à-dire les mesures). Mais je pense que les performances, même si elles sont à l'échelle du micro-gain, méritent d'être mentionnées en tant que bonus avec une conception fondamentalement supérieure.
R .. GitHub ARRÊTEZ D'AIDER LA GLACE

13
const char* byte_to_binary( int x )
{
    static char b[sizeof(int)*8+1] = {0};
    int y;
    long long z;
    for (z=1LL<<sizeof(int)*8-1,y=0; z>0; z>>=1,y++)
    {
        b[y] = ( ((x & z) == z) ? '1' : '0');
    }

    b[y] = 0;

    return b;
}

6
Plus clair si vous utilisez '1'et '0'au lieu de 49et 48dans votre ternaire. Il bdoit également contenir 9 caractères afin que le dernier caractère puisse rester un terminateur nul.
tomlogic

De plus, B doit être initialisé à chaque fois.
EvilTeach

2
Pas si vous changez certains: 1. ajouter de l' espace pour un zéro final: static char b[9] = {0}2. Déclaration de déplacement de la boucle: int z,y;3. Ajouter le zéro final: b[y] = 0. De cette façon, aucune revitalisation n'est nécessaire.
Kobor42

1
Belle solution. Je changerais cependant certaines choses. C'est-à-dire revenir en arrière dans la chaîne afin que les entrées de toute taille puissent être gérées correctement.
Kobor42

Tous ces 8s devraient être remplacés par CHAR_BIT.
alk

12

Solution simple et rapide:

void printbits(my_integer_type x)
{
    for(int i=sizeof(x)<<3; i; i--)
        putchar('0'+((x>>(i-1))&1));
}

Fonctionne pour tout type de taille et pour les encres signées et non signées. Le '& 1' est nécessaire pour gérer les entrées signées car le décalage peut faire l'extension du signe.

Il y a tellement de façons de procéder. Voici un super simple pour imprimer 32 bits ou n bits à partir d'un type 32 bits signé ou non signé (ne pas mettre de négatif s'il est signé, juste imprimer les bits réels) et pas de retour chariot. Notez que i est décrémenté avant le décalage de bit:

#define printbits_n(x,n) for (int i=n;i;i--,putchar('0'|(x>>i)&1))
#define printbits_32(x) printbits_n(x,32)

Qu'en est-il du retour d'une chaîne avec les bits à stocker ou à imprimer plus tard? Vous pouvez soit allouer la mémoire et la renvoyer et l'utilisateur doit la libérer, soit renvoyer une chaîne statique mais elle sera encombrée si elle est appelée à nouveau, ou par un autre thread. Les deux méthodes présentées:

char *int_to_bitstring_alloc(int x, int count)
{
    count = count<1 ? sizeof(x)*8 : count;
    char *pstr = malloc(count+1);
    for(int i = 0; i<count; i++)
        pstr[i] = '0' | ((x>>(count-1-i))&1);
    pstr[count]=0;
    return pstr;
}

#define BITSIZEOF(x)    (sizeof(x)*8)

char *int_to_bitstring_static(int x, int count)
{
    static char bitbuf[BITSIZEOF(x)+1];
    count = (count<1 || count>BITSIZEOF(x)) ? BITSIZEOF(x) : count;
    for(int i = 0; i<count; i++)
        bitbuf[i] = '0' | ((x>>(count-1-i))&1);
    bitbuf[count]=0;
    return bitbuf;
}

Appeler avec:

// memory allocated string returned which needs to be freed
char *pstr = int_to_bitstring_alloc(0x97e50ae6, 17);
printf("bits = 0b%s\n", pstr);
free(pstr);

// no free needed but you need to copy the string to save it somewhere else
char *pstr2 = int_to_bitstring_static(0x97e50ae6, 17);
printf("bits = 0b%s\n", pstr2);

10

Aucune des réponses précédemment publiées ne correspond exactement à ce que je cherchais, alors j'en ai écrit une. Il est super simple d'utiliser% B avec le printf!

    /*
     * File:   main.c
     * Author: Techplex.Engineer
     *
     * Created on February 14, 2012, 9:16 PM
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <printf.h>
    #include <math.h>
    #include <string.h>


    static int printf_arginfo_M(const struct printf_info *info, size_t n, int *argtypes) {
        /* "%M" always takes one argument, a pointer to uint8_t[6]. */
        if (n > 0) {
            argtypes[0] = PA_POINTER;
        }
        return 1;
    } /* printf_arginfo_M */

    static int printf_output_M(FILE *stream, const struct printf_info *info, const void *const *args) {
        int value = 0;
        int len;

        value = *(int **) (args[0]);

        //Beginning of my code ------------------------------------------------------------
        char buffer [50] = ""; //Is this bad?
        char buffer2 [50] = ""; //Is this bad?
        int bits = info->width;
        if (bits <= 0)
            bits = 8; // Default to 8 bits

        int mask = pow(2, bits - 1);
        while (mask > 0) {
            sprintf(buffer, "%s", (((value & mask) > 0) ? "1" : "0"));
            strcat(buffer2, buffer);
            mask >>= 1;
        }
        strcat(buffer2, "\n");
        // End of my code --------------------------------------------------------------
        len = fprintf(stream, "%s", buffer2);
        return len;
    } /* printf_output_M */

    int main(int argc, char** argv) {

        register_printf_specifier('B', printf_output_M, printf_arginfo_M);

        printf("%4B\n", 65);

        return (EXIT_SUCCESS);
    }

1
ce débordement avec plus de 50 bits?
Janus Troelsen

Bon appel, ouais ça le sera ... On m'a dit que je devais utiliser du malloc, jamais en faire?
TechplexEngineer

Oui bien sûr. super facile:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen

@JanusTroelsen, ou beaucoup plus propre, plus petit, maintenable:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz

Pourquoi «% B» serait-il différent de «% b» à cet égard? Les réponses précédentes disaient des choses comme "Il n'y a pas de fonction de formatage dans la bibliothèque standard C pour sortir du binaire comme ça." et " Certains runtimes prennent en charge"% b "bien que ce ne soit pas une norme." .
Peter Mortensen


7

Ce code devrait gérer vos besoins jusqu'à 64 bits. J'ai créé 2 fonctions pBin & pBinFill. Les deux font la même chose, mais pBinFill remplit les espaces de tête avec fillChar. La fonction de test génère des données de test, puis les imprime à l'aide de la fonction.



char* pBinFill(long int x,char *so, char fillChar); // version with fill
char* pBin(long int x, char *so);                   // version without fill
#define kDisplayWidth 64

char* pBin(long int x,char *so)
{
 char s[kDisplayWidth+1];
 int  i=kDisplayWidth;
 s[i--]=0x00;   // terminate string
 do
 { // fill in array from right to left
  s[i--]=(x & 1) ? '1':'0';  // determine bit
  x>>=1;  // shift right 1 bit
 } while( x > 0);
 i++;   // point to last valid character
 sprintf(so,"%s",s+i); // stick it in the temp string string
 return so;
}

char* pBinFill(long int x,char *so, char fillChar)
{ // fill in array from right to left
 char s[kDisplayWidth+1];
 int  i=kDisplayWidth;
 s[i--]=0x00;   // terminate string
 do
 { // fill in array from right to left
  s[i--]=(x & 1) ? '1':'0';
  x>>=1;  // shift right 1 bit
 } while( x > 0);
 while(i>=0) s[i--]=fillChar;    // fill with fillChar 
 sprintf(so,"%s",s);
 return so;
}

void test()
{
 char so[kDisplayWidth+1]; // working buffer for pBin
 long int val=1;
 do
 {
   printf("%ld =\t\t%#lx =\t\t0b%s\n",val,val,pBinFill(val,so,'0'));
   val*=11; // generate test data
 } while (val < 100000000);
}

Output:
00000001 =  0x000001 =  0b00000000000000000000000000000001
00000011 =  0x00000b =  0b00000000000000000000000000001011
00000121 =  0x000079 =  0b00000000000000000000000001111001
00001331 =  0x000533 =  0b00000000000000000000010100110011
00014641 =  0x003931 =  0b00000000000000000011100100110001
00161051 =  0x02751b =  0b00000000000000100111010100011011
01771561 =  0x1b0829 =  0b00000000000110110000100000101001
19487171 = 0x12959c3 =  0b00000001001010010101100111000011

1
"#define width 64" est en conflit avec stream.h de log4cxx. S'il vous plaît, utilisez des noms définis conventionnellement au hasard :)
kagali-san

5
@mhambra: vous devez désactiver log4cxx pour utiliser un nom générique à la widthplace!
u0b34a0f6ae

7

Existe-t-il un convertisseur printf pour imprimer au format binaire?

La printf()famille ne peut imprimer qu'en base 8, 10 et 16 en utilisant directement les spécificateurs standard. Je suggère de créer une fonction qui convertit le nombre en une chaîne selon les besoins particuliers du code.


Pour imprimer dans n'importe quelle base [2-36]

Jusqu'à présent, toutes les autres réponses ont au moins une de ces limitations.

  1. Utilisez de la mémoire statique pour le tampon de retour. Cela limite le nombre de fois où la fonction peut être utilisée comme argument printf().

  2. Allouez de la mémoire nécessitant le code appelant aux pointeurs libres.

  3. Exiger que le code appelant fournisse explicitement un tampon approprié.

  4. Appelez printf()directement. Cela oblige une nouvelle fonction à fprintf(), sprintf(), vsprintf(), etc.

  5. Utilisez une plage entière réduite.

Ce qui suit n'a aucune des limitations ci-dessus . Il nécessite C99 ou version ultérieure et l'utilisation de "%s". Il utilise un littéral composé pour fournir l'espace tampon. Il n'a aucun problème avec plusieurs appels dans un printf().

#include <assert.h>
#include <limits.h>
#define TO_BASE_N (sizeof(unsigned)*CHAR_BIT + 1)

//                               v. compound literal .v
#define TO_BASE(x, b) my_to_base((char [TO_BASE_N]){""}, (x), (b))

// Tailor the details of the conversion function as needed
// This one does not display unneeded leading zeros
// Use return value, not `buf`
char *my_to_base(char *buf, unsigned i, int base) {
  assert(base >= 2 && base <= 36);
  char *s = &buf[TO_BASE_N - 1];
  *s = '\0';
  do {
    s--;
    *s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % base];
    i /= base;
  } while (i);

  // Could employ memmove here to move the used buffer to the beginning

  return s;
}

#include <stdio.h>
int main(void) {
  int ip1 = 0x01020304;
  int ip2 = 0x05060708;
  printf("%s %s\n", TO_BASE(ip1, 16), TO_BASE(ip2, 16));
  printf("%s %s\n", TO_BASE(ip1, 2), TO_BASE(ip2, 2));
  puts(TO_BASE(ip1, 8));
  puts(TO_BASE(ip1, 36));
  return 0;
}

Production

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

C'est très utile. Savez-vous comment l'utiliser en C ++? Lorsque je compile, il génère une erreur "Gravity Code Description Project File Line Suppression State Error C4576 un type entre parenthèses suivi d'une liste d'initialisation est une syntaxe de conversion de type explicite non standard bonjour C: \ mes_projets \ bonjour \ bonjour \ main.cpp 39 "
Juste un apprenant le

1
@Justalearner Cela génère un C ++ car s'il utilise un littéral composé de fonctionnalité C qui ne fait pas partie de C ++. Peut-être publier votre implémentation C ++ qui essaie de faire la même chose - même si elle est incomplète, je suis sûr que vous obtiendrez de l'aide - tant que vous montrez votre tentative en premier.
chux

6

Peut-être un peu OT, mais si vous en avez besoin uniquement pour le débogage pour comprendre ou retracer certaines opérations binaires que vous faites, vous pouvez jeter un œil à wcalc (une simple calculatrice de console). Avec les options -b, vous obtenez une sortie binaire.

par exemple

$ wcalc -b "(256 | 3) & 0xff"
 = 0b11

1
il y a aussi quelques autres options sur ce front ... ruby -e 'printf("%b\n", 0xabc)', dcsuivies de 2osuivies par 0x123pet ainsi de suite.
lindes

6

Il n'y a pas de fonction de formatage dans la bibliothèque standard C pour sortir un binaire comme ça. Toutes les opérations de formatage prises en charge par la famille printf visent un texte lisible par l'homme.


5

La fonction récursive suivante peut être utile:

void bin(int n)
{
    /* Step 1 */
    if (n > 1)
        bin(n/2);
    /* Step 2 */
    printf("%d", n % 2);
}

7
Attention, cela ne fonctionne pas avec des entiers négatifs.
Anderson Freitas

4

J'ai optimisé la meilleure solution pour la taille et le C ++ - ness, et suis arrivé à cette solution:

inline std::string format_binary(unsigned int x)
{
    static char b[33];
    b[32] = '\0';

    for (int z = 0; z < 32; z++) {
        b[31-z] = ((x>>z) & 0x1) ? '1' : '0';
    }

    return b;
}

3
Si vous souhaitez utiliser la mémoire dynamique (à travers std::string), vous pouvez tout aussi bien vous débarrasser du statictableau. Le moyen le plus simple serait de simplement supprimer le staticqualificatif et de le rendre blocal à la fonction.
Shahbaz

((x>>z) & 0x01) + '0'est suffisant.
Jason C

4

Imprimer des bits de tout type en utilisant moins de code et de ressources

Cette approche a pour attributs:

  • Fonctionne avec des variables et des littéraux.
  • N'itère pas tous les bits lorsqu'ils ne sont pas nécessaires.
  • Appelez printf uniquement lorsque vous complétez un octet (pas inutilement pour tous les bits).
  • Fonctionne pour tout type.
  • Fonctionne avec une petite et grande endianité (utilise GCC #defines pour la vérification).
  • Utilise typeof () qui n'est pas la norme C mais qui est largement défini.
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define for_endian(size) for (int i = 0; i < size; ++i)
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define for_endian(size) for (int i = size - 1; i >= 0; --i)
#else
#error "Endianness not detected"
#endif

#define printb(value)                                   \
({                                                      \
        typeof(value) _v = value;                       \
        __printb((typeof(_v) *) &_v, sizeof(_v));       \
})

void __printb(void *value, size_t size)
{
        uint8_t byte;
        size_t blen = sizeof(byte) * 8;
        uint8_t bits[blen + 1];

        bits[blen] = '\0';
        for_endian(size) {
                byte = ((uint8_t *) value)[i];
                memset(bits, '0', blen);
                for (int j = 0; byte && j < blen; ++j) {
                        if (byte & 0x80)
                                bits[j] = '1';
                        byte <<= 1;
                }
                printf("%s ", bits);
        }
        printf("\n");
}

int main(void)
{
        uint8_t c1 = 0xff, c2 = 0x44;
        uint8_t c3 = c1 + c2;

        printb(c1);
        printb((char) 0xff);
        printb((short) 0xff);
        printb(0xff);
        printb(c2);
        printb(0x44);
        printb(0x4411ff01);
        printb((uint16_t) c3);
        printf("\n");

        return 0;
}

Production

$ ./printb 
11111111 
11111111 
00000000 11111111 
00000000 00000000 00000000 11111111 
01000100 
00000000 00000000 00000000 01000100 
01000100 00010001 11111111 00000001 
00000000 01000011 

J'ai utilisé une autre approche ( bitprint.h ) pour remplir une table avec tous les octets (sous forme de chaînes de bits) et les imprimer en fonction de l'octet d'entrée / d'index. Ça vaut le coup d'oeil.


4
void
print_binary(unsigned int n)
{
    unsigned int mask = 0;
    /* this grotesque hack creates a bit pattern 1000... */
    /* regardless of the size of an unsigned int */
    mask = ~mask ^ (~mask >> 1);

    for(; mask != 0; mask >>= 1) {
        putchar((n & mask) ? '1' : '0');
    }

}

Ou ajoutez 0 ou 1 à la valeur de caractère de '0';) Pas de ternaire nécessaire.
Chouette

3

J'ai aimé le code de paniq, le tampon statique est une bonne idée. Cependant, il échoue si vous voulez plusieurs formats binaires dans un seul printf () car il retourne toujours le même pointeur et écrase le tableau.

Voici un drop-in de style C qui fait pivoter le pointeur sur un tampon divisé.

char *
format_binary(unsigned int x)
{
    #define MAXLEN 8 // width of output format
    #define MAXCNT 4 // count per printf statement
    static char fmtbuf[(MAXLEN+1)*MAXCNT];
    static int count = 0;
    char *b;
    count = count % MAXCNT + 1;
    b = &fmtbuf[(MAXLEN+1)*count];
    b[MAXLEN] = '\0';
    for (int z = 0; z < MAXLEN; z++) { b[MAXLEN-1-z] = ((x>>z) & 0x1) ? '1' : '0'; }
    return b;
}

1
Une fois countatteint MAXCNT - 1, l'incrément suivant de countle fera MAXCNTau lieu de zéro, ce qui entraînera un accès hors des limites du tableau. Vous auriez dû faire count = (count + 1) % MAXCNT.
Shahbaz

1
Soit dit en passant, cela surprendrait plus tard un développeur qui utilise les MAXCNT + 1appels à cette fonction en un seul printf. En général, si vous voulez donner l'option pour plus d'une chose, rendez-la infinie. Des nombres tels que 4 ne pouvaient que poser problème.
Shahbaz

3

Pas de manière standard et portable.

Certaines implémentations fournissent itoa () , mais ce ne sera pas le cas dans la plupart des cas, et il a une interface quelque peu minable. Mais le code est derrière le lien et devrait vous permettre d'implémenter votre propre formateur assez facilement.


3

Conversion générique d'une instruction de tout type intégral en représentation de chaîne binaire à l'aide de la bibliothèque standard :

#include <bitset>
MyIntegralType  num = 10;
print("%s\n",
    std::bitset<sizeof(num) * 8>(num).to_string().insert(0, "0b").c_str()
); // prints "0b1010\n"

Ou juste: std::cout << std::bitset<sizeof(num) * 8>(num);


1
C'est une solution idiomatique pour C ++ mais il demandait C.
danijar

3

Ma solution:

long unsigned int i;
for(i = 0u; i < sizeof(integer) * CHAR_BIT; i++) {
    if(integer & LONG_MIN)
        printf("1");
    else
        printf("0");
    integer <<= 1;
}
printf("\n");

3

Sur la base de @ suggestion de ideasman42 dans sa réponse, il est une macro qui fournit int8, 16, 32et 64versions, réutilisant les INT8macro pour éviter la répétition.

/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_SEPARATOR
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8               PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

#include <stdio.h>
int main() {
    long long int flag = 1648646756487983144ll;
    printf("My Flag "
           PRINTF_BINARY_PATTERN_INT64 "\n",
           PRINTF_BYTE_TO_BINARY_INT64(flag));
    return 0;
}

Cela produit:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Pour plus de lisibilité, vous pouvez changer: #define PRINTF_BINARY_SEPARATORen #define PRINTF_BINARY_SEPARATOR ","ou#define PRINTF_BINARY_SEPARATOR " "

Cela produira:

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000

ou

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

merci de copier ce code, le premier à copier dans ce projet sur
lequel


2
void print_ulong_bin(const unsigned long * const var, int bits) {
        int i;

        #if defined(__LP64__) || defined(_LP64)
                if( (bits > 64) || (bits <= 0) )
        #else
                if( (bits > 32) || (bits <= 0) )
        #endif
                return;

        for(i = 0; i < bits; i++) { 
                printf("%lu", (*var >> (bits - 1 - i)) & 0x01);
        }
}

devrait fonctionner - non testé.


2
/* Convert an int to it's binary representation */

char *int2bin(int num, int pad)
{
 char *str = malloc(sizeof(char) * (pad+1));
  if (str) {
   str[pad]='\0';
   while (--pad>=0) {
    str[pad] = num & 1 ? '1' : '0';
    num >>= 1;
   }
  } else {
   return "";
  }
 return str;
}

/* example usage */

printf("The number 5 in binary is %s", int2bin(5, 4));
/* "The number 5 in binary is 0101" */

4
Payer le coût d'une mallocation nuira à la performance. Passer la responsabilité de la destruction de la mémoire tampon à l'appelant n'est pas gentil.
EvilTeach

2

Ensuite, vous verrez la disposition de la mémoire:

#include <limits>
#include <iostream>
#include <string>

using namespace std;

template<class T> string binary_text(T dec, string byte_separator = " ") {
    char* pch = (char*)&dec;
    string res;
    for (int i = 0; i < sizeof(T); i++) {
        for (int j = 1; j < 8; j++) {
            res.append(pch[i] & 1 ? "1" : "0");
            pch[i] /= 2;
        }
        res.append(byte_separator);
    }
    return res;
}

int main() {
    cout << binary_text(5) << endl;
    cout << binary_text(.1) << endl;

    return 0;
}

Que voulez-vous dire par "Suivant vous montrera la disposition de la mémoire" ?
Peter Mortensen

2

Voici une petite variante de la solution de paniq qui utilise des modèles pour permettre l'impression d'entiers 32 et 64 bits:

template<class T>
inline std::string format_binary(T x)
{
    char b[sizeof(T)*8+1] = {0};

    for (size_t z = 0; z < sizeof(T)*8; z++)
        b[sizeof(T)*8-1-z] = ((x>>z) & 0x1) ? '1' : '0';

    return std::string(b);
}

Et peut être utilisé comme:

unsigned int value32 = 0x1e127ad;
printf( "  0x%x: %s\n", value32, format_binary(value32).c_str() );

unsigned long long value64 = 0x2e0b04ce0;
printf( "0x%llx: %s\n", value64, format_binary(value64).c_str() );

Voici le résultat:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.