Esiste un convertitore printf per stampare in formato binario?


435

Posso stampare con printf come un numero esadecimale o ottale. Esiste un tag di formato da stampare come binario o base arbitraria?

Sto eseguendo gcc.

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

Per quanto ne so, non puoi farlo usando printf. Potresti, ovviamente, scrivere un metodo di supporto per raggiungere questo obiettivo, ma non sembra la direzione in cui vuoi andare.
Ian P,

Non esiste un formato predefinito per questo. Devi trasformarlo da solo in una stringa e quindi stampare la stringa.
rslite,

Una rapida ricerca su Google ha prodotto questa pagina con alcune informazioni che potrebbero essere utili: forums.macrumors.com/archive/index.php/t-165959.html
Ian P

12
Non come parte della libreria ANSI standard C: se stai scrivendo un codice portatile, il metodo più sicuro è quello di creare il tuo.
tomlogic

Uno standard di dichiarazione e generico (per qualsiasi tipo integrale di qualsiasi lunghezza) la soluzione della conversione di stringa binaria su C ++: stackoverflow.com/a/31660310/1814353
Luart

Risposte:


267

Hacky ma funziona per me:

#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));

Per tipi multi-byte

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

Purtroppo hai bisogno di tutte le citazioni extra. Questo approccio presenta i rischi di efficienza delle macro (non passare una funzione come argomento a BYTE_TO_BINARY) ma evita i problemi di memoria e le invocazioni multiple di strcat in alcune delle altre proposte qui.


13
E ha anche il vantaggio di essere invocabile più volte in un caso in printfcui quelli con staticbuffer non possono.
Patrick Schlüter,

4
Mi sono preso la libertà di cambiare %din %c, perché dovrebbe essere ancora più veloce ( %ddeve eseguire la conversione digit-> char, mentre %cgenera semplicemente l'argomento

3
Pubblicato una versione estesa di questa macro con il 16, 32, il supporto a 64 bit int: stackoverflow.com/a/25108449/432509
ideasman42

2
Si noti che questo approccio non è compatibile con lo stack. Supponendo che intil sistema sia a 32 bit, la stampa di un singolo valore a 32 bit richiederà spazio per valori di 32 * 4 byte; totale di 128 byte. Che, a seconda delle dimensioni dello stack, può essere o meno un problema.
user694733

1
è importante aggiungere parentesi attorno al byte nella macro o potresti incorrere in problemi durante l'invio di un'operazione BYTE_TO_BINARY (a | b) -> a | b & 0x01! = (a | b) & 0x01
Ivan Hoffmann,

203

Stampa binaria per qualsiasi tipo di dati

//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("");
}

test

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
Suggerisci size_t i; for (i=size; i-- > 0; )di evitare size_tvs. intmis-match.
chux - Ripristina Monica il

1
Qualcuno potrebbe approfondire la logica dietro questo codice?
jII

2
Accetta ogni byte in ptr(loop esterno); quindi per ogni bit il byte corrente (loop interno), maschera il byte dal bit corrente ( 1 << j). Sposta quel diritto risultante in un byte contenente 0 ( 0000 0000b) o 1 ( 0000 0001b). Stampa il byte risultante printf con il formato %u. HTH.
Nielsbot,

1
@ ZX9 Si noti che il codice suggerito utilizzato >con size_te non quello >=del commento per determinare quando terminare il ciclo.
chux - Ripristina Monica il

3
@ ZX9 Ancora un tuo utile commento originale in quanto i programmatori devono stare attenti considerando l'uso del case edge di >e>= con tipi non firmati. 0è un caso limite senza segno e si verifica comunemente, a differenza della matematica firmata con meno comune INT_MAX/INT_MIN.
chux - Ripristina Monica il

151

Ecco un trucco rapido per dimostrare le tecniche per fare ciò che vuoi.

#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
Questo è certamente meno "strano" della scrittura personalizzata di un sovraccarico di escape per printf. È anche semplice da capire per uno sviluppatore nuovo nel codice.
Furious Coder,

43
Alcune modifiche: strcatè un metodo inefficiente per aggiungere un singolo carattere alla stringa su ogni passaggio del ciclo. Invece, aggiungi a char *p = b;e sostituisci il ciclo interno con *p++ = (x & z) ? '1' : '0'. zdovrebbe iniziare da 128 (2 ^ 7) anziché 256 (2 ^ 8). Considerare l'aggiornamento per utilizzare un puntatore al buffer da utilizzare (per la sicurezza del thread), in modo simile a inet_ntoa().
tomlogic

3
@EvilTeach: stai usando un operatore ternario come parametro per strcat()! Concordo sul fatto che strcatprobabilmente è più facile da capire rispetto al post-incremento di un puntatore senza riferimenti per il compito, ma anche i principianti devono sapere come utilizzare correttamente la libreria standard. Forse usare un array indicizzato per l'assegnazione sarebbe stata una buona dimostrazione (e funzionerà davvero, dato che bnon viene resettato a tutti gli zeri ogni volta che chiami la funzione).
tomlogic

3
Casuale: il carattere del buffer binario è statico e viene cancellato da tutti gli zeri nell'assegnazione. Questo lo cancellerà solo la prima volta che viene eseguito, e successivamente non verrà cancellato, ma utilizzerà invece l'ultimo valore.
markwatson,

8
Inoltre, questo dovrebbe documentare che il risultato precedente non sarà valido dopo la chiamata di nuovo la funzione, in modo da chiamanti non dovrebbe cercare di usarlo in questo modo: printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann,

84

Normalmente non c'è un identificatore di conversione binaria in glibc.

È possibile aggiungere tipi di conversione personalizzati alla famiglia di funzioni printf () in glibc. Vedi register_printf_function per i dettagli. È possibile aggiungere una conversione% b personalizzata per uso personale, se semplifica il codice dell'applicazione per renderlo disponibile.

Ecco un esempio di come implementare formati di stampa personalizzati in glibc.


Ho sempre scritto la mia v [snf] printf () per i casi limitati in cui volevo diversi radix: così felice di averlo sfogliato.
Jamie,

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]C'è una nuova funzione di fare lo stesso, però: register_printf_specifier(). Un esempio del nuovo utilizzo è disponibile qui: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito

47

È possibile utilizzare una piccola tabella per migliorare la velocità 1 . Tecniche simili sono utili nel mondo incorporato, ad esempio, per invertire un byte:

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 Mi riferisco principalmente ad applicazioni integrate in cui gli ottimizzatori non sono così aggressivi e la differenza di velocità è visibile.


27

Stampa il bit meno significativo e spostalo verso destra. In questo modo fino a quando l'intero diventa zero, la rappresentazione binaria viene stampata senza zeri iniziali ma in ordine inverso. Utilizzando la ricorsione, l'ordine può essere corretto abbastanza facilmente.

#include <stdio.h>

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

Per me, questa è una delle soluzioni più pulite al problema. Se ti piace il 0bprefisso e un nuovo carattere di riga finale, ti suggerisco di racchiudere la funzione.

Demo online


errore: troppi argomenti per far funzionare la chiamata, previsto 2, hanno 1 putc ((numero & 1)? '1': '0');
Koray Tugay,

@KorayTugay Grazie per averlo segnalato. Ho corretto la chiamata di funzione e ho aggiunto una demo.
danijar,

6
dovresti anche usare un numero int senza segno, perché quando il dato numero è negativo, la funzione entra in una chiamata ricorsiva senza fine.
Puffy,

Approccio più efficiente, poiché in ASCII, '0' + 1 = '1':putc('0'+(number&1), stdout);
Roger Dueck,

22

Sulla base di risposta di @William Whyte, questa è una macro che fornisce int8, 16, 32e 64le versioni, il riutilizzo della INT8macro per evitare ripetizioni.

/* --- 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;
}

Questo produce:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Per motivi di leggibilità, potresti voler aggiungere un separatore, ad esempio:

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

Questo è eccellente C'è un motivo particolare per stampare i bit che iniziano con i bit meno significativi?
gaganso,

2
come consiglieresti di aggiungere la virgola?
nmz787,

Aggiungerebbe una versione raggruppata di PRINTF_BYTE_TO_BINARY_INT#definisce da utilizzare facoltativamente.
ideasman42

16

Ecco una versione della funzione che non soffre di problemi di rientro o limiti sulla dimensione / tipo dell'argomento:

#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;
}

Si noti che questo codice funzionerebbe altrettanto bene per qualsiasi base tra 2 e 10 se si sostituissero i 2 con la base desiderata. L'utilizzo è:

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

Dov'è xqualsiasi espressione integrale.


7
Sì, puoi farlo. Ma è davvero un brutto design. Anche se non si hanno thread o rientri, il chiamante deve essere consapevole che il buffer statico viene riutilizzato e che cose del genere char *a = binary_fmt(x), *b = binary_fmt(y);non funzioneranno come previsto. Forzare il chiamante a passare un buffer rende esplicito il requisito di archiviazione; il chiamante è ovviamente libero di usare un buffer statico se ciò è veramente desiderato, e quindi il riutilizzo dello stesso buffer diventa esplicito. Si noti inoltre che, nelle moderne ABI PIC, i buffer statici di solito costano più codice per l'accesso rispetto ai buffer nello stack.
R .. GitHub smette di aiutare ICE il

8
È ancora un cattivo design. Richiede una fase di copia aggiuntiva in questi casi e non è meno costoso del fatto che il chiamante fornisca il buffer anche nei casi in cui non sarebbe richiesta la copia. L'uso dell'archiviazione statica è solo un brutto idioma.
R .. GitHub smette di aiutare ICE il

3
Dover inquinare lo spazio dei nomi del preprocessore o della tabella dei simboli variabili con un nome aggiuntivo non necessario che deve essere utilizzato per dimensionare correttamente l'archiviazione che deve essere allocata da ogni chiamante e costringere ogni chiamante a conoscere questo valore e allocare la quantità necessaria di storage, è una cattiva progettazione quando la soluzione di storage locale-funzione più semplice sarà sufficiente per la maggior parte degli scopi e quando una semplice chiamata strdup () copre il 99% degli altri utilizzi.
Greg A. Woods,

5
Qui non dovremo essere d'accordo. Non riesco a vedere come l'aggiunta di un simbolo di preprocessore discreto si avvicini alla dannosità di limitare gravemente i casi di utilizzo, rendendo l'interfaccia soggetta a errori, riservando una memoria permanente per la durata del programma per un valore temporaneo e generando codice peggiore sulla maggior parte piattaforme moderne.
R .. GitHub smette di aiutare ICE il

5
Non sostengo la microottimizzazione senza motivo (es. Misurazioni). Ma penso che le prestazioni, anche se sono sulla scala del micro guadagno, meritano di essere menzionate quando si tratta di un bonus insieme a un design fondamentalmente superiore.
R .. GitHub smette di aiutare ICE il

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
Più chiaro se usi '1'e '0'invece di 49e 48nel tuo ternario. Inoltre, bdovrebbe essere lungo 9 caratteri in modo che l'ultimo carattere possa rimanere un terminatore nullo.
tomlogic

Anche B deve essere inizializzato ogni volta.
EvilTeach

2
No, se si cambia un po ': 1. spazio aggiuntivo per uno zero finale: static char b[9] = {0}2. Dichiarazione mossa fuori dal giro: int z,y;3. Aggiungere la finale pari a zero: b[y] = 0. In questo modo non è necessaria alcuna reinizializzazione.
Kobor42,

1
Bella soluzione. Vorrei cambiare alcune cose però. Vale a dire tornare indietro nella stringa in modo che input di qualsiasi dimensione possano essere gestiti correttamente.
Kobor42,

Tutti questi messaggi 8dovrebbero essere sostituiti da CHAR_BIT.
alk

12

Soluzione semplice e veloce:

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

Funziona con qualsiasi tipo di dimensione e per interi firmati e non firmati. "& 1" è necessario per gestire gli integri firmati poiché lo spostamento può comportare l'estensione dei segni.

Ci sono molti modi per farlo. Eccone uno semplicissimo per stampare 32 bit o n bit da un tipo a 32 bit con o senza segno (non mettere un negativo se firmato, solo stampare i bit effettivi) e nessun ritorno a capo. Si noti che i è decrementato prima del bit shift:

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

Che ne dici di restituire una stringa con i bit da memorizzare o stampare in seguito? È possibile allocare la memoria e restituirla e l'utente deve liberarla, oppure si restituisce una stringa statica ma verrà ostruita se viene richiamata nuovamente o da un altro thread. Entrambi i metodi mostrati:

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;
}

Chiama con:

// 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

Nessuna delle risposte precedentemente pubblicate è esattamente quello che stavo cercando, quindi ne ho scritto uno. È super semplice usare% B con il 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
questo trabocca con più di 50 bit?
Janus Troelsen,

Bella chiamata, sì lo sarà ... Mi è stato detto che dovevo usare malloc, non lo hai mai fatto?
TechplexEngineer,

sì, naturalmente. super facile:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen il

@JanusTroelsen, o molto più pulito, più piccolo, mantenibile:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz

Perché "% B" sarebbe diverso da "% b" sotto questo aspetto? Le risposte precedenti dicevano cose del tipo "Non esiste una funzione di formattazione nella libreria standard C per produrre binari del genere." e " Alcuni runtime supportano"% b "sebbene non sia uno standard." .
Peter Mortensen,


7

Questo codice dovrebbe gestire le tue esigenze fino a 64 bit. Ho creato 2 funzioni pBin & pBinFill. Entrambi fanno la stessa cosa, ma pBinFill riempie gli spazi iniziali con fillChar. La funzione di test genera alcuni dati di test, quindi li stampa utilizzando la funzione.



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" è in conflitto con stream.h di log4cxx. Per favore, usa i nomi convenzionalmente casuali :)
kagali-san

5
@mhambra: dovresti cancellare log4cxx per aver usato un nome così generico come widthinvece!
u0b34a0f6ae

7

Esiste un convertitore printf per stampare in formato binario?

La printf()famiglia è in grado di stampare solo nelle basi 8, 10 e 16 utilizzando direttamente gli identificatori standard. Suggerisco di creare una funzione che converta il numero in una stringa per esigenze particolari del codice.


Per stampare in qualsiasi base [2-36]

Tutte le altre risposte finora hanno almeno una di queste limitazioni.

  1. Utilizzare la memoria statica per il buffer di ritorno. Ciò limita il numero di volte in cui la funzione può essere utilizzata come argomento printf().

  2. Allocare memoria che richiede il codice chiamante per liberare i puntatori.

  3. Richiede che il codice chiamante fornisca esplicitamente un buffer adatto.

  4. Chiama printf()direttamente. Questo obbliga una nuova funzione per la a fprintf(), sprintf(), vsprintf(), etc.

  5. Utilizzare un intervallo intero ridotto.

Quanto segue non presenta alcuna delle suddette limitazioni . Richiede C99 o versioni successive e l'utilizzo di "%s". Utilizza un valore letterale composto per fornire lo spazio buffer. Non ha problemi con più chiamate in a 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;
}

Produzione

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

Questo è molto utile Sai come usarlo in C ++? Durante la compilazione, viene generato un errore "Codice di gravità Descrizione Errore stato stato soppressione riga file progetto C4576 un tipo tra parentesi seguito da un elenco di inizializzatori è una sintassi di conversione di tipo esplicito non standard ciao c: \ my_projects \ hello \ hello \ main.cpp 39 "
Solo uno studente

1
@Justalearner Questo genera un C ++ perché se utilizza un valore letterale composto C che non fa parte di C ++. Forse pubblica la tua implementazione C ++ che cerca di fare lo stesso - anche se incompleta, sono sicuro che otterrai aiuto - purché mostri prima il tuo tentativo.
chux - Ripristina Monica il

6

Forse un po 'OT, ma se ne hai bisogno solo per il debug per capire o ripercorrere alcune operazioni binarie che stai facendo, potresti dare un'occhiata a wcalc (una semplice calcolatrice console). Con le opzioni -b ottieni un output binario.

per esempio

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

1
ci sono anche alcune altre opzioni su questo fronte ... ruby -e 'printf("%b\n", 0xabc)', dcseguite da 2oseguite da 0x123pe così via.
termina il

6

Non esiste alcuna funzione di formattazione nella libreria standard C per generare binari del genere. Tutte le operazioni di formattazione supportate dalla famiglia printf riguardano il testo leggibile dall'uomo.


5

Potrebbe essere utile la seguente funzione ricorsiva:

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

7
Fai attenzione, questo non funziona con numeri interi negativi.
Anderson Freitas,

4

Ho ottimizzato la soluzione migliore per dimensioni e C ++ e sono arrivato a questa soluzione:

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
Se si desidera utilizzare la memoria dinamica (tramite std::string), è possibile eliminare l' staticarray. Il modo più semplice sarebbe quello di rilasciare il staticqualificatore e renderlo blocale alla funzione.
Shahbaz,

((x>>z) & 0x01) + '0'è sufficiente.
Jason C,

4

Stampa bit di qualsiasi tipo utilizzando meno codice e risorse

Questo approccio ha come attributi:

  • Funziona con variabili e valori letterali.
  • Non esegue l'iterazione di tutti i bit quando non è necessario.
  • Chiama printf solo quando completa un byte (non inutilmente per tutti i bit).
  • Funziona con qualsiasi tipo.
  • Funziona con piccole e grandi endianness (usa GCC #defines per il controllo).
  • Utilizza typeof () che non è standard C ma è ampiamente definito.
#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;
}

Produzione

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

Ho usato un altro approccio ( bitprint.h ) per riempire una tabella con tutti i byte (come stringhe di bit) e stamparli in base al byte di input / indice. Vale la pena dare un'occhiata.


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');
    }

}

Oppure aggiungi 0 o 1 al valore del carattere di '0';) Nessun ternario necessario.
Gufo

3

Mi è piaciuto il codice di Paniq, il buffer statico è una buona idea. Tuttavia, non riesce se si desidera più formati binari in un singolo printf () perché restituisce sempre lo stesso puntatore e sovrascrive l'array.

Ecco un drop-in in stile C che ruota il puntatore su un buffer diviso.

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
Una volta countraggiunto MAXCNT - 1, l'incremento successivo di countlo renderebbe MAXCNTinvece di zero, il che provocherebbe un accesso fuori dai limiti dell'array. Avresti dovuto count = (count + 1) % MAXCNT.
Shahbaz,

1
A proposito, questo sarebbe una sorpresa in seguito per uno sviluppatore che utilizza le MAXCNT + 1chiamate a questa funzione in un unico printf. In generale, se vuoi dare l'opzione per più di 1 cosa, rendila infinita. Numeri come 4 potrebbero solo causare problemi.
Shahbaz,

3

Nessun modo standard e portatile.

Alcune implementazioni forniscono itoa () , ma non sarà nella maggior parte, e ha un'interfaccia un po 'scadente. Ma il codice è dietro il link e dovrebbe consentirti di implementare il tuo formatter abbastanza facilmente.


3

Una conversione generica di un'istruzione di qualsiasi tipo integrale nella rappresentazione di stringa binaria utilizzando la libreria 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"

O semplicemente: std::cout << std::bitset<sizeof(num) * 8>(num);


1
Questa è una soluzione idiomatica per C ++, ma stava chiedendo C.
danijar il

3

La mia soluzione:

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

Sulla base di @ ideasman42 suggestione nella sua risposta, questa è una macro che fornisce int8, 16, 32e 64le versioni, il riutilizzo della INT8macro per evitare ripetizioni.

/* --- 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;
}

Questo produce:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Per la leggibilità è possibile modificare: #define PRINTF_BINARY_SEPARATORa#define PRINTF_BINARY_SEPARATOR "," o#define PRINTF_BINARY_SEPARATOR " "

Questo produrrà:

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

o

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

grazie copiando questo codice, il primo da copiare in questo progetto a cui sto lavorando, scrivere questo mi è sembrato un compito noioso :)
DevZer0


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);
        }
}

dovrebbe funzionare - non testato.


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
Pagare il costo di una mallocazione danneggerà le prestazioni. Passare la responsabilità per la distruzione del buffer al chiamante non è gentile.
EvilTeach

2

Successivamente verrà mostrato il layout di memoria:

#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;
}

Cosa intendi con "Next mostrerà il tuo layout di memoria" ?
Peter Mortensen,

2

Ecco una piccola variante della soluzione di paniq che utilizza modelli per consentire la stampa di numeri interi a 32 e 64 bit:

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);
}

E può essere usato come:

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() );

Ecco il risultato:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.