Existe um conversor printf para imprimir em formato binário?


434

Posso imprimir com printf como um número hexadecimal ou octal. Existe uma etiqueta de formato para imprimir como base binária ou arbitrária?

Estou executando o gcc.

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

Você não pode fazer isso, tanto quanto eu sei, usando printf. Obviamente, você poderia escrever um método auxiliar para fazer isso, mas isso não soa como a direção que você deseja seguir.
Ian P

Não há um formato predefinido para isso. Você precisa transformá-lo em uma sequência e depois imprimi-la.
rslite 21/09/08

Uma rápida pesquisa no Google produziu esta página com algumas informações que podem ser úteis: forums.macrumors.com/archive/index.php/t-165959.html
Ian P

12
Não faz parte da Biblioteca ANSI Standard C - se você estiver escrevendo código portátil, o método mais seguro é criar o seu.
tomlogic

Um padrão de declaração e genérica (para qualquer tipo Integral de qualquer comprimento) solução da conversão para string binária em C ++: stackoverflow.com/a/31660310/1814353
luart

Respostas:


266

Hacky, mas funciona para mim:

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

Para tipos de bytes múltiplos

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

Infelizmente, você precisa de todas as cotações extras. Essa abordagem apresenta os riscos de eficiência das macros (não passe uma função como argumento BYTE_TO_BINARY), mas evita os problemas de memória e várias invocações do strcat em algumas das outras propostas aqui.


13
E tem a vantagem de ser invocável várias vezes, em printfque os que possuem staticbuffers não podem.
Patrick Schlüter

4
Eu tomei a liberdade de mudar o %dque %c, porque ele deve ser ainda mais rápido ( %dtem que executar digit-> caractere de conversão, enquanto que %csimplesmente exibe o argumento

3
Publicou uma versão expandida dessa macro com suporte int de 16, 32 e 64 bits: stackoverflow.com/a/25108449/432509
ideasman42

2
Observe que essa abordagem não é compatível com pilhas. Supondo que haja int32 bits no sistema, a impressão de um valor único de 32 bits exigirá espaço para valores de 32 * 4 bytes; total de 128 bytes. O que, dependendo do tamanho da pilha, pode ou não ser um problema.
user694733

1
é importante adicionar parênteses em torno de bytes na macro ou você pode ter problemas ao enviar uma operação BYTE_TO_BINARY (a | b) -> a | b & 0x01! = (a | b) & 0x01
Ivan Hoffmann

203

Binário de impressão para qualquer tipo de dados

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

teste

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
Sugira size_t i; for (i=size; i-- > 0; )para evitar size_tvs. intincompatibilidade.
chux - Restabelece Monica

1
Alguém poderia, por favor, elaborar a lógica por trás desse código?
jii

2
Coloque cada byte em ptr(loop externo); depois, para cada bit, o byte atual (loop interno), oculte o byte pelo bit atual ( 1 << j). Mude para a direita, resultando em um byte contendo 0 ( 0000 0000b) ou 1 ( 0000 0001b). Imprima o byte resultante printf com o formato %u. HTH.
Nielsbot

1
@ ZX9 Observe que o código sugerido foi usado >com size_te não com o >=seu comentário para determinar quando terminar o loop.
chux - Restabelece Monica

3
@ ZX9 Ainda é um comentário original útil para você, pois os codificadores precisam ter cuidado, considerando o uso de casos extremos >e >=com tipos não assinados. 0é um caso de borda não assinado e geralmente ocorre, ao contrário da matemática assinada com menos comum INT_MAX/INT_MIN.
chux - Restabelece Monica

151

Aqui está um truque rápido para demonstrar técnicas para fazer o que você deseja.

#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
Isso é certamente menos "estranho" do que o costume de escrever uma sobrecarga de escape para printf. É simples de entender para um desenvolvedor novo no código também.
Furious Coder

43
Algumas mudanças: strcaté um método ineficiente de adicionar um único caractere à string em cada passagem do loop. Em vez disso, adicione char *p = b;ae substitua o loop interno por *p++ = (x & z) ? '1' : '0'. zdeve começar em 128 (2 ^ 7) em vez de 256 (2 ^ 8). Considere atualizar para levar um ponteiro para o buffer a ser usado (para segurança do thread), semelhante a inet_ntoa().
tomlogic

3
@EvilTeach: Você está usando um operador ternário como parâmetro para strcat()! Concordo que strcaté provavelmente mais fácil de entender do que o pós-incremento de um ponteiro não referenciado para a tarefa, mas mesmo os iniciantes precisam saber como usar adequadamente a biblioteca padrão. Talvez o uso de uma matriz indexada para atribuição tenha sido uma boa demonstração (e realmente funcionará, pois bnão é redefinido para todos os zeros toda vez que você chama a função).
tomlogic

3
Aleatório: O caractere de buffer binário é estático e é limpo para todos os zeros na atribuição. Isso só limpará na primeira vez em que for executado, e depois disso não será limpo, mas, em vez disso, use o último valor.
markwatson

8
Além disso, este deve documentar que o resultado anterior será inválido depois de chamar a função novamente, para que os chamadores não deve tentar usá-lo como este: printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Pa Elo Ebermann 30/07

84

Não há um especificador de conversão binário na glibc normalmente.

É possível adicionar tipos de conversão personalizados à família de funções printf () na glibc. Consulte register_printf_function para obter detalhes. Você pode adicionar uma conversão% b personalizada para seu próprio uso, se simplificar o código do aplicativo para disponibilizá-lo.

Aqui está um exemplo de como implementar um formato printf personalizado na glibc.


Eu sempre escrevi meu próprio v [snf] printf () para os casos limitados em que queria radixs diferentes: tão feliz por ter folheado isso.
21715 Jamie

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Há uma nova função para fazer o mesmo, no entanto: register_printf_specifier(). Um exemplo do novo uso pode ser encontrado aqui: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito

47

Você pode usar uma pequena mesa para melhorar a velocidade 1 . Técnicas semelhantes são úteis no mundo incorporado, por exemplo, para inverter um 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 Estou me referindo principalmente a aplicativos incorporados nos quais os otimizadores não são tão agressivos e a diferença de velocidade é visível.


27

Imprima o bit menos significativo e desloque-o para a direita. Fazer isso até que o número inteiro se torne zero imprime a representação binária sem zeros à esquerda, mas em ordem inversa. Usando a recursão, a ordem pode ser corrigida facilmente.

#include <stdio.h>

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

Para mim, esta é uma das soluções mais limpas para o problema. Se você gosta de 0bprefixo e um caractere de nova linha à direita, sugiro agrupar a função.

Demonstração online


erro: muito poucos argumentos para chamar a função, esperado 2, possuem 1 putc ((número & 1)? '1': '0');
precisa saber é o seguinte

@KorayTugay Obrigado por apontar isso. Corrigi a chamada de função e adicionei uma demonstração.
danijar

6
você também deve usar um número int não assinado, porque quando o número fornecido é negativo, a função entra em uma chamada recursiva interminável.
Inchaço

Abordagem mais eficiente, já que em ASCII, '0' + 1 = '1':putc('0'+(number&1), stdout);
Roger Dueck

22

Com base na resposta de @William Whyte, esta é uma macro que fornece int8, 16, 32e 64versões, reutilizando a INT8macro à repetição evitar.

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

Isso gera:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Para facilitar a leitura, você pode adicionar um separador, por exemplo:

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

Isto e excelente. Existe uma razão específica para imprimir os bits começando com Menos bits significativos?
gaganso

2
como você recomendaria adicionar a vírgula?
precisa saber é o seguinte

Adicionaria uma versão agrupada de PRINTF_BYTE_TO_BINARY_INT#define para usar opcionalmente.
ideasman42

16

Aqui está uma versão da função que não sofre problemas de reentrada ou limites no tamanho / tipo do argumento:

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

Observe que esse código funcionaria tão bem para qualquer base entre 2 e 10 se você apenas substituir os 2 pela base desejada. O uso é:

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

Onde xestá qualquer expressão integral.


7
Sim, você pode fazer isso. Mas é um design muito ruim. Mesmo se você não tiver threads ou reentrada, o chamador deve estar ciente de que o buffer estático está sendo reutilizado e que coisas assim char *a = binary_fmt(x), *b = binary_fmt(y);não funcionarão conforme o esperado. Forçar o chamador a passar um buffer torna explícito o requisito de armazenamento; é claro que o chamador está livre para usar um buffer estático, se isso for realmente desejado, e a reutilização do mesmo buffer se torna explícita. Observe também que, nas ABIs PIC modernas, os buffers estáticos geralmente custam mais código para acessar do que os buffers na pilha.
R .. GitHub Pare de ajudar o gelo

8
Ainda é um design ruim. Requer uma etapa extra de cópia nesses casos, e não é menos caro do que o chamador fornecer o buffer, mesmo nos casos em que a cópia não seria necessária. Usar armazenamento estático é apenas um idioma ruim.
R .. GitHub Pare de ajudar o gelo

3
É necessário poluir o espaço para nome da tabela de símbolos do pré-processador ou da variável com um nome extra desnecessário que deve ser usado para dimensionar adequadamente o armazenamento que deve ser alocado por cada chamador e forçar cada chamador a conhecer esse valor e alocar a quantidade necessária de armazenamento, é um design incorreto quando a solução mais simples de armazenamento local de funções é suficiente para a maioria das intenções e propósitos e quando uma chamada simples strdup () cobre 99% do restante dos usos.
Greg A. Woods,

5
Aqui vamos ter que discordar. Não consigo ver como a adição de um símbolo discreto de pré-processador chega perto da nocividade de limitar severamente os casos de uso, tornando a interface propensa a erros, reservando armazenamento permanente para a duração do programa por um valor temporário e gerando código pior na maioria dos casos. plataformas modernas.
R .. GitHub Pare de ajudar o gelo

5
Não defendo a micro-otimização sem razão (isto é, medições). Mas acho que o desempenho, mesmo que seja na escala de micro-ganho, vale a pena mencionar quando se trata de um bônus junto com um design fundamentalmente superior.
R .. GitHub Pare de ajudar o gelo

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
Mais claro se você usar '1'e em '0'vez de 49e 48em seu ternário. Além disso, bdeve ter 9 caracteres, para que o último caractere possa permanecer um terminador nulo.
tomlogic

Também B precisa ser inicializado a cada vez.
EvilTeach

2
Não se você mudar alguns: 1. Espaço add para uma final zero: static char b[9] = {0}2. declaração movimento fora do circuito: int z,y;3. Adicione a final zero: b[y] = 0. Dessa forma, não é necessária reinitalização.
Kobor42

1
Ótima solução. Eu mudaria algumas coisas embora. Ou seja, voltando para trás na string, para que entradas de qualquer tamanho possam ser manipuladas corretamente.
precisa saber é o seguinte

Todos esses 8s devem ser substituídos por CHAR_BIT.
alk

12

Solução rápida e fácil:

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

Funciona para qualquer tipo de tamanho e para entradas assinadas e não assinadas. O '& 1' é necessário para manipular entradas registradas, pois a mudança pode significar a extensão.

Existem muitas maneiras de fazer isso. Aqui está um super simples para imprimir 32 bits ou n bits de um tipo assinado ou não assinado de 32 bits (sem colocar negativo se assinado, apenas imprimindo os bits reais) e sem retorno de carro. Observe que i é decrementado antes da mudança 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)

Que tal retornar uma string com os bits para armazenar ou imprimir posteriormente? Você pode alocar a memória e devolvê-la e o usuário precisa liberá-la; caso contrário, você retorna uma sequência estática, mas ela será derrotada se for chamada novamente ou por outro encadeamento. Ambos os métodos mostrados:

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

Ligue para:

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

Nenhuma das respostas postadas anteriormente é exatamente o que eu estava procurando, então escrevi uma. É super simples de usar% B com o 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
isso vai estourar com mais de 50 bits?
Janus Troelsen

Boa ligação, sim, vai ... me disseram que eu precisava usar malloc, já vestiu isso?
TechplexEngineer

sim, claro. super fácil:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen 22/03

@JanusTroelsen, ou muito mais limpo, menor, manutenível:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz

Por que "% B" seria diferente de "% b" nesse aspecto? Respostas anteriores diziam coisas como "Não há função de formatação na biblioteca padrão C para gerar binário como esse". e " Alguns tempos de execução suportam"% b ", embora isso não seja um padrão." .
Peter Mortensen


7

Este código deve lidar com suas necessidades de até 64 bits. Eu criei 2 funções pBin & pBinFill. Ambos fazem a mesma coisa, mas o pBinFill preenche os espaços iniciais com o fillChar. A função de teste gera alguns dados de teste e os imprime usando a função.



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" entra em conflito com stream.h do log4cxx. Por favor, use convencionalmente aleatória definir nomes :)
kagali-san

5
@ mhambra: você deve avisar log4cxx por usar um nome tão genérico como o widthseu!
u0b34a0f6ae

7

Existe um conversor printf para imprimir em formato binário?

A printf()família só pode imprimir nas bases 8, 10 e 16 usando diretamente os especificadores padrão. Sugiro criar uma função que converta o número em uma string de acordo com as necessidades particulares do código.


Para imprimir em qualquer base [2-36]

Todas as outras respostas até agora têm pelo menos uma dessas limitações.

  1. Use memória estática para o buffer de retorno. Isso limita o número de vezes que a função pode ser usada como argumento printf().

  2. Aloque memória que requer o código de chamada para liberar ponteiros.

  3. Exija que o código de chamada forneça explicitamente um buffer adequado.

  4. Ligue printf()diretamente. Isto obriga uma nova função para a fprintf(), sprintf(), vsprintf(), etc.

  5. Use um intervalo inteiro reduzido.

O seguinte não possui nenhuma das limitações acima . Requer C99 ou posterior e uso de "%s". Ele usa um literal composto para fornecer o espaço no buffer. Não há problemas com várias chamadas em 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;
}

Resultado

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

Isso é muito útil. Você sabe como usá-lo em C ++? Quando eu compilar, ele gera um erro "C4576, um tipo entre parênteses seguido por uma lista de inicializadores é uma sintaxe de conversão de tipo explícita não padrão olá C: \ my_projects \ hello \ hello \ main.cpp 39 "
Apenas um aprendiz

1
@Justalearner Isso gera um C ++ porque se usa um literal composto de recurso C que não faz parte do C ++. Talvez poste sua implementação em C ++ que tente fazer o mesmo - mesmo que incompleta, tenho certeza de que você obterá ajuda - desde que mostre sua tentativa primeiro.
chux - Restabelece Monica

6

Talvez um pouco OT, mas se você precisar disso apenas para depuração para entender ou refazer algumas operações binárias que você está fazendo, você pode dar uma olhada no wcalc (uma calculadora simples do console). Com as opções -b, você obtém saída binária.

por exemplo

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

1
também há algumas outras opções nessa frente ... ruby -e 'printf("%b\n", 0xabc)', dcseguidas por 2oseguidas 0x123pe assim por diante.
Lindes

6

Não há nenhuma função de formatação na biblioteca padrão C para gerar binário assim. Todas as operações de formato suportadas pela família printf são direcionadas para texto legível por humanos.


5

A seguinte função recursiva pode ser útil:

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

7
Cuidado, isso não funciona com números inteiros negativos.
Anderson Freitas

4

Otimizei a melhor solução para tamanho e C ++, e cheguei a esta solução:

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 você quiser usar a memória dinâmica (por meio de std::string), também poderá se livrar da staticmatriz. A maneira mais simples seria simplesmente largar o staticqualificador e tornar blocal a função.
Shahbaz

((x>>z) & 0x01) + '0'é suficiente.
Jason C

4

Imprima bits de qualquer tipo usando menos código e recursos

Essa abordagem tem como atributos:

  • Funciona com variáveis ​​e literais.
  • Não itera todos os bits quando não é necessário.
  • Chame printf somente quando concluir um byte (não desnecessariamente para todos os bits).
  • Funciona para qualquer tipo.
  • Funciona com pouca e grande utilidade (usa o GCC #defines para verificação).
  • Usa typeof () que não é padrão C, mas é amplamente definido.
#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;
}

Resultado

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

Eu usei outra abordagem ( bitprint.h ) para preencher uma tabela com todos os bytes (como cadeias de bits) e imprimi-los com base no byte de entrada / índice. Vale a pena dar uma olhada.


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 adicione 0 ou 1 ao valor do caractere '0';) Não é necessário ternário.
Owl #

3

Gostei do código do paniq, o buffer estático é uma boa ideia. No entanto, ele falhará se você desejar vários formatos binários em um único printf (), pois sempre retorna o mesmo ponteiro e sobrescreve a matriz.

Aqui está um drop-in no estilo C que gira o ponteiro em um buffer dividido.

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
Uma vez countatingido MAXCNT - 1, o próximo incremento de countfaria em MAXCNTvez de zero, o que causará um acesso fora dos limites da matriz. Você deveria ter feito count = (count + 1) % MAXCNT.
Shahbaz

1
A propósito, isso seria uma surpresa mais tarde para um desenvolvedor que usa MAXCNT + 1chamadas para essa função em um único printf. Em geral, se você quiser dar a opção para mais de uma coisa, torne-a infinita. Números como 4 só podem causar problemas.
Shahbaz 24/09

3

Nenhuma maneira padrão e portátil.

Algumas implementações fornecem itoa () , mas não será a maioria, e possui uma interface um pouco ruim. Mas o código está por trás do link e deve permitir que você implemente seu próprio formatador com bastante facilidade.


3

Conversão genérica de uma instrução de qualquer tipo integral na representação de cadeia binária usando a biblioteca padrão :

#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 apenas: std::cout << std::bitset<sizeof(num) * 8>(num);


1
Essa é uma solução idiomática para C ++, mas ele estava pedindo C.
danijar

3

Minha solução:

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

Baseado em @ de ideasman42 sugestão em sua resposta, esta é uma macro que fornece int8, 16, 32e 64versões, reutilizando a INT8macro à repetição evitar.

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

Isso gera:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Para facilitar a leitura, você pode alterar: #define PRINTF_BINARY_SEPARATORpara #define PRINTF_BINARY_SEPARATOR ","ou#define PRINTF_BINARY_SEPARATOR " "

Isso produzirá:

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

ou

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

obrigado copiando este código, o primeiro a copiar neste projeto em que estou trabalhando, escrevendo esta tarefa tediosa :)
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);
        }
}

deve funcionar - não testado.


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
Pagar o custo de uma alocação prejudicará o desempenho. Passar a responsabilidade pela destruição do buffer para o chamador é desagradável.
21711 EvilTeach

2

Em seguida, mostrará o layout da memória:

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

O que você quer dizer com "Próximo mostrará o layout da memória" ?
Peter Mortensen

2

Aqui está uma pequena variação da solução da paniq que usa modelos para permitir a impressão de números inteiros de 32 e 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);
}

E pode ser usado como:

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

Aqui está o resultado:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.