Чи є конвертер printf для друку у двійковому форматі?


434

Я можу друкувати разом із printf у вигляді шістнадцяткового або восьмеричного номера. Чи є тег формату для друку у вигляді двійкової чи довільної бази?

Я бігаю GCC.

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

Ви не можете цього зробити, наскільки я знаю, використовуючи printf. Очевидно, ви можете написати допоміжний метод для цього, але це не схоже на напрямок, який ви хочете піти.
Ян P

Для цього не визначений формат. Вам потрібно самостійно перетворити його на рядок, а потім надрукувати рядок.
rslite

Швидкий пошук Google створив цю сторінку з деякою інформацією, яка може бути корисною: forums.macrumors.com/archive/index.php/t-165959.html
Ian P

12
Не є частиною бібліотеки ANSI Standard C - якщо ви пишете портативний код, найбезпечнішим методом є прокрутка власного.
tomlogic

Один стандартного заяву і загальні (для будь-якого інтегрального типу будь-якої довжини) рішення перетворення в двійкову рядок на C ++: stackoverflow.com/a/31660310/1814353
luart

Відповіді:


266

Хаккі, але працює для мене:

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

Для багатобайтових типів

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

На жаль, вам потрібні всі додаткові цитати. Цей підхід містить ризики ефективності макросів (не передайте функцію як аргумент BYTE_TO_BINARY), але дозволяє уникнути проблем із пам'яттю та декількох викликів strcat в деяких інших пропозиціях тут.


13
І перевага також полягає в тому, що вони можуть викликати кілька разів, у printfяких ті, що мають staticбуфери, не можуть.
Патрік Шлютер

4
Я взяв на себе змогу змінити %dна %c, тому що він повинен бути ще швидшим ( %dдоводиться виконувати перетворення цифр> та символів, а %cпросто виводить аргумент

3
Розміщена розширена версія цього макросу з 16, 32, 64 битого INT підтримки: stackoverflow.com/a/25108449/432509
ideasman42

2
Зауважте, що цей підхід не є дружнім. Якщо intв системі 32-бітне значення, для друку одного 32-бітного значення знадобиться місце для 32 * 4-байтних значень; всього 128 байт. Що, залежно від розміру стека, може бути, а може і не бути проблемою.
користувач694733

1
важливо додати круглі дужки навколо байта в макрос, або у вас можуть виникнути проблеми при надсиланні операції BYTE_TO_BINARY (a | b) -> a | b & 0x01! = (a | b) & 0x01
Іван Гофманн

203

Друкуйте двійковий код для будь-якого типу даних

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

тест

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
Запропонуйте size_t i; for (i=size; i-- > 0; )уникати size_tпроти intневідповідних матчів.
chux

1
Може хтось, будь ласка, детальніше розробив логіку цього коду?
jII

2
Взяти кожен байт у ptr(зовнішня петля); то для кожного біта поточний байт (внутрішній цикл) маскуйте байт поточним бітом ( 1 << j). Зсуньте праворуч, у результаті чого байт містить 0 ( 0000 0000b) або 1 ( 0000 0001b). Друкуйте отриманий байт printf з форматом %u. HTH.
nielsbot

1
@ ZX9 Зверніть увагу , що запропонований код використовується >з , size_tа не >=вашим коментарем , щоб визначити , коли для завершення циклу.
chux

3
@ ZX9 Тим НЕ менше корисний оригінальний коментар ваших , як кодерам дійсно потрібно бути обережними , приймаючи у увагу випадку використання Краї >і >=з беззнаковими типами. 0є непідписаним крайовим випадком і зазвичай трапляється, на відміну від підписаної математики з менш поширеною INT_MAX/INT_MIN.
chux

151

Ось швидкий злом для демонстрації прийомів робити те, що ви хочете.

#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
Це, звичайно, менш «дивно», ніж написання на замовлення надрукування для printf. Це легко зрозуміти і для розробника, нового для коду.
Furious Coder

43
Кілька змін: strcatце неефективний метод додавання одного символу до рядка на кожному проході циклу. Натомість додайте а char *p = b;та замініть внутрішню петлю *p++ = (x & z) ? '1' : '0'. zслід починати з 128 (2 ^ 7) замість 256 (2 ^ 8). Розгляньте можливість оновлення, щоб взяти вказівник на буфер, що використовується (для безпеки потоку), подібний до inet_ntoa().
tomlogic

3
@EvilTeach: Ви самі використовуєте потрійний оператор як параметр для strcat()! Я погоджуюсь, що strcatце, мабуть, простіше зрозуміти, ніж після збільшення індеребрированного покажчика для призначення, але навіть початківцям потрібно знати, як правильно використовувати стандартну бібліотеку. Можливо, використання індексованого масиву для призначення було б хорошою демонстрацією (і насправді спрацює, оскільки bне скидається на всі нулі щоразу, коли ви викликаєте функцію).
tomlogic

3
Випадково: Значок двійкового буфера є статичним і очищається від усіх нулів у призначенні. Це очистить його лише при першому запуску, а після цього не буде зрозумілим, але натомість використовувати останнє значення.
Markwatson

8
Крім того , це має документально підтвердити , що попередній результат буде недійсним після повторного виклику функції, тому абоненти не повинні намагатися використовувати його як це: printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann

84

Нормально не існує двійкового специфікатора перетворення в glibc.

До сімейства функцій printf () в glibc можна додати спеціальні типи перетворення. Докладніше див. У розділі register_printf_function . Ви можете додати спеціальну конверсію% b для власного використання, якщо вона спростить код програми, щоб вона була доступною.

Ось приклад того, як реалізувати власні формати printf у glibc.


Я завжди писав власний v [snf] printf () для обмежених випадків, коли мені потрібні різні радикси: так що я радив переглядати це.
Джеймі

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Існує нова функція , щоб зробити те ж саме, але: register_printf_specifier(). Приклад нового використання можна знайти тут: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito

47

Ви можете використовувати невелику таблицю для покращення швидкості 1 . Подібні методи корисні у вбудованому світі, наприклад, для інвертування байта:

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 Я в основному зі посиланням на вбудовані додатки , де оптимізатори не так агресивні і різниця в швидкості видно.


27

Надрукуйте найменш значущий біт і змістіть його праворуч. Роблячи це, поки ціле число не стане нульовим, друкує двійкове представлення без провідних нулів, але в зворотному порядку. Використовуючи рекурсію, порядок можна виправити досить легко.

#include <stdio.h>

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

Для мене це одне з найчистіших рішень проблеми. Якщо вам подобається 0bпрефікс і символ нового рядка, я пропоную переключити функцію.

Демонстрація в Інтернеті


помилка: занадто мало аргументів для функціонування виклику, очікується 2, мають 1 putc ((число & 1)? '1': '0');
Корай Тугай

@KorayTugay Дякую, що вказали на це. Я виправив виклик функції та додав демонстраційну версію.
danijar

6
ви також повинні використовувати непідписаний int номер, оскільки коли задане число від'ємне, функція вводить у нескінченний рекурсивний виклик.
Puffy

Більш ефективний підхід, оскільки в ASCII '0' + 1 = '1':putc('0'+(number&1), stdout);
Роджер Дуек

22

На підставі відповіді @William Уайта, це макрос , який забезпечує int8, 16, 32&64 версії, повторне використання в INT8макрос , щоб уникнути повторення.

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

Цей результат:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Для читабельності ви можете додати роздільник для:

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

Це чудово. Чи є якась причина для друку бітів, починаючи з найменш значущих бітів?
gaganso

2
як би ти рекомендував додавати кому?
nmz787

Додано б групувану версію PRINTF_BYTE_TO_BINARY_INT#визначень для необов'язкового використання.
ideaman42

16

Ось версія функції, яка не страждає від проблем перенавчання або обмежень щодо розміру / типу аргументу:

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

Зауважте, що цей код буде працювати так само добре для будь-якої бази від 2 до 10, якщо ви просто заміните 2 на бажану базу. Використання:

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

Де xє будь-яке цілісне вираження.


7
Так, ви можете це зробити. Але це дійсно поганий дизайн. Навіть якщо у вас немає ниток або перезапису, абонент повинен усвідомлювати, що статичний буфер використовується повторно, і такі речі char *a = binary_fmt(x), *b = binary_fmt(y);не працюватимуть, як очікувалося. Примушення абонента передавати буфер робить вимогу зберігання explict; звичайно, абонент може безкоштовно використовувати статичний буфер, якщо це дійсно бажано, і тоді повторне використання того ж буфера стає явним. Також зауважте, що на сучасних PIC ABI статичні буфери зазвичай коштують більше коду для доступу, ніж буфери в стеці.
R .. GitHub СТОП ДОПОМОГАЙТЕ

8
Це все-таки поганий дизайн. У цих випадках потрібен додатковий крок копіювання, і це не менш дорого, ніж дозволити абоненту надавати буфер навіть у випадках, коли копіювання не потрібно. Використання статичного зберігання - лише погана ідіома.
R .. GitHub СТОП ДОПОМОГАЄТЬСЯ

3
Необхідність забруднювати простір імен або препроцесора, або таблиці символьних змінних зайвою додатковою назвою, яка повинна використовуватися для належного розміру сховища, який повинен бути призначений кожним абонентом, і змушує кожного абонента знати це значення та виділяти необхідну кількість зберігання, це поганий дизайн, коли більш простого рішення локального зберігання функцій вистачить для більшості намірів і цілей, а коли простий виклик strdup () охоплює 99% решти випадків використання.
Грег А. Вудс

5
Тут нам доведеться не погодитися. Я не бачу, як додавання одного ненав'язливого символу препроцесора приходить десь поблизу шкідливості обмеження випадків використання, роблячи інтерфейс схильним до помилок, зберігаючи постійне зберігання протягом тривалості програми для тимчасового значення та генеруючи гірший код для більшості сучасні платформи.
R .. GitHub ЗАСТАНОВИТЬ ДІЙЛИ

5
Я не виступаю за мікрооптимізацію без причин (тобто вимірювань). Але я думаю, що продуктивність, навіть якщо вона на шкалі мікро-посилення, варто згадати, коли вона є бонусом разом із принципово покращеним дизайном.
R .. GitHub СТОП ДОПОМОГАТИ ЛИЦЯ

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
Чіткіше, якщо ви використовуєте '1'і '0'замість, 49і 48в своєму поживнику. Також bмає бути 9 символів, щоб останній символ міг залишатися нульовим термінатором.
tomlogic

Також B потрібно кожного разу ініціалізувати.
EvilTeach

2
Ні , якщо ви змінити деякі з них : 1. додати простору для остаточної нульового: static char b[9] = {0}2. декларації вийти з циклу: int z,y;3. Додайте остаточний нуль: b[y] = 0. Таким чином не потрібна реінізація.
Кобор42

1
Приємне рішення. Я хотів би змінити деякі речі, хоча. Тобто йде назад в рядку, щоб введення будь-якого розміру могло бути оброблено належним чином.
Kobor42

Усі ці 8s повинні бути замінені на CHAR_BIT.
алк

12

Швидке та просте рішення:

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

Працює для будь-якого типу розміру та для підписаних та неподписаних вкладок. '& 1' потрібен для обробки підписаних вводів, оскільки зсув може робити розширення знаків.

Існує так багато способів зробити це. Ось надзвичайно простий варіант для друку 32 біт або n бітів із підписаного або непідписаного 32-бітного типу (не ставити мінус, якщо він підписаний, просто надрукувати фактичні біти) і не повернути каретку. Зауважте, що i зменшується до зсуву біт:

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

Що з поверненням рядка з бітами для зберігання чи друку пізніше? Ви можете виділити пам'ять і повернути її, і користувач повинен її звільнити, або ж ви повернете статичну рядок, але вона буде розблокована, якщо вона буде викликана ще раз, або іншим потоком. Показані обидва способи:

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

Телефонуйте за допомогою:

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

Жодна з раніше опублікованих відповідей не є саме тим, що я шукав, тому я написав одну. Використовувати% B разом із 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
це переповнюється більш ніж 50 бітами?
Янус Троельсен

Добрий дзвінок, так, це буде ... Мені сказали, що мені потрібно використовувати malloc, коли-небудь робити це?
TechplexEngineer

так, звісно. супер легко:char* buffer = (char*) malloc(sizeof(char) * 50);
Янус Трольсен

@JanusTroelsen, або набагато чистіший, менший, ремонтабельний:char *buffer = malloc(sizeof(*buffer) * 50);
Шахбаз

Чому в цьому відношенні "% B" відрізняється від "% b"? У попередніх відповідях говорилося про такі речі, як "У стандартній бібліотеці C немає функції форматування для виведення подібного двійкового файлу". і " Деякі періоди виконання підтримують"% b ", хоча це не є стандартом." .
Пітер Мортенсен

8

Деякі періоди виконання підтримують "% b", хоча це не є стандартом.

Також дивіться тут цікаву дискусію:

http://bytes.com/forum/thread591027.html

HTH


1
Це насправді властивість бібліотеки виконання C, а не компілятора.
cjm

7

Цей код повинен відповідати вашим потребам до 64 біт. Я створив 2 функції pBin & pBinFill. Обидва роблять те саме, але pBinFill заповнює провідні простори за допомогою fillChar. Тестова функція генерує деякі тестові дані, а потім роздруковує їх за допомогою функції.



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" конфліктує з stream.h від log4cxx. Будь ласка, використовуйте умовно випадкові імена :)
kagali-san

5
@mhambra: ви повинні сказати log4cxx вимкнено для використання такого загального імені, widthзамість цього!
u0b34a0f6ae

7

Чи є конвертер printf для друку у двійковому форматі?

printf()Сім'я тільки в стані друкувати в базі 8, 10 і 16 з використанням стандартних специфікаторів безпосередньо. Я пропоную створити функцію, яка перетворює число в рядок відповідно до конкретних потреб коду.


Друкувати в будь-якій базі [2-36]

Усі інші відповіді поки що мають хоча б одне з цих обмежень.

  1. Використовуйте статичну пам'ять для зворотного буфера. Це обмежує кількість разів, яку функція може використовуватись як аргумент printf().

  2. Виділіть пам'ять, яка вимагає коду виклику, до вільних покажчиків.

  3. Потрібно, щоб код виклику явно надав відповідний буфер.

  4. Телефонуйте printf()безпосередньо. Це зобов'язує нову функцію до fprintf(), sprintf(), vsprintf()і т.д.

  5. Використовуйте зменшений цілий діапазон.

Далі не має жодного з перерахованих вище обмежень . Це вимагає C99 або пізнішої версії та використання "%s". Для використання буферного простору він використовує складний літерал . Немає проблем із кількома дзвінками в 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;
}

Вихідні дані

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

Це дуже корисно. Чи знаєте ви, як його використовувати в C ++? Коли я компілюю, він генерує помилку "Код тяжкості Опис Повідомлення Лінія файлу проекту Помилка Помилка C4576, тип в дужках з подальшим списком ініціалізатора є нестандартним синтаксисом явного перетворення типу привіт C: \ my_projects \ hello \ hello \ main.cpp 39 "
Просто учень

1
@Justalearner Це генерує C ++, тому що якщо використовується C- компонентний літерал, який не є частиною C ++. Можливо, опублікуйте вашу C ++ реалізацію, яка намагається зробити те саме - навіть якщо вона неповна, я впевнений, що вам допоможуть - доки ви спершу покажете свою спробу.
chux

6

Можливо, трохи OT, але якщо вам це потрібно лише для налагодження, щоб зрозуміти або відтворити деякі бінарні операції, які ви робите, ви можете подивитися на wcalc (простий калькулятор консолі). За допомогою параметрів -b ви отримуєте двійковий вихід.

напр

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

1
на цьому фронті є також кілька інших варіантів ... ruby -e 'printf("%b\n", 0xabc)', dcза якими 2oслідує 0x123p, і так далі.
lindes

6

У стандартній бібліотеці C немає функції форматування для виведення подібного двійкового файлу. Всі операції формату, які підтримує сімейство printf, спрямовані на текст, читаний людиною.


5

Наступна рекурсивна функція може бути корисною:

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

7
Будьте уважні, це не працює з негативними цілими числами.
Андерсон Фрейтас

4

Я оптимізував найкраще рішення для розміру та C ++ - ності, і дійшов до цього рішення:

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
Якщо ви хочете використовувати динамічну пам'ять (через std::string), ви також можете позбутися staticмасиву. Найпростішим способом було б просто скинути staticкласифікатор і зробити bлокальну для функції.
Шахбаз

((x>>z) & 0x01) + '0'є достатнім.
Джейсон C

4

Друкуйте біти будь-якого типу, використовуючи менше коду та ресурсів

Цей підхід має такі ознаки:

  • Працює зі змінними та літералами.
  • Не повторює всі біти, коли це не потрібно.
  • Викликайте printf лише тоді, коли буде завершено байт (не зайво для всіх біт).
  • Працює для будь-якого типу.
  • Працює з малою та великою небезпекою (для перевірки використовує GCC #defines).
  • Використовує typeof (), що не є стандартом C, але багато в чому визначено.
#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;
}

Вихідні дані

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

Я використовував інший підхід ( bitprint.h ), щоб заповнити таблицю усіма байтами (як бітові рядки) та роздрукувати їх на байті введення / індексу. Варто поглянути.


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

}

Або додайте 0 або 1 до значення символу '0';) Потрійний не потрібен.
Сова

3

Мені сподобався код від paniq, статичний буфер - хороша ідея. Однак він не вдається, якщо потрібно кілька двійкових форматів в одному printf (), оскільки він завжди повертає один і той же покажчик і перезаписує масив.

Ось спадне меню у стилі C, яке обертає вказівник на розділений буфер.

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
Після countдосягнення MAXCNT - 1наступного приросту, countвін зробить його MAXCNTзамість нуля, що призведе до виходу за межі масиву. Ви повинні були зробити count = (count + 1) % MAXCNT.
Шахбаз

1
До речі, це пізніше стане сюрпризом для розробника, який використовує MAXCNT + 1дзвінки до цієї функції в єдиному printf. Загалом, якщо ви хочете дати варіант для більше ніж 1 речі, зробіть це нескінченним. Числа, такі як 4, можуть спричинити лише проблеми.
Шахбаз

3

Немає стандартного та портативного способу.

Деякі реалізації надають itoa () , але в більшості він не буде, і він має дещо крихкий інтерфейс. Але код знаходиться за посиланням, і він повинен дозволити вам легко реалізувати власний формат.


3

Одне твердження загальне перетворення будь-якого інтегрального типу у представлення бінарних рядків за допомогою стандартної бібліотеки:

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

Або просто: std::cout << std::bitset<sizeof(num) * 8>(num);


1
Це ідіоматичне рішення для C ++, але він просив C.
danijar

3

Моє рішення:

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

На основі @ ideasman42 - х навіювання в своїй відповіді, це макрос , який забезпечує int8, 16, 32& 64версії, повторне використання в INT8макрос , щоб уникнути повторення.

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

Цей результат:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Для читабельності ви можете змінити: #define PRINTF_BINARY_SEPARATORна#define PRINTF_BINARY_SEPARATOR "," або#define PRINTF_BINARY_SEPARATOR " "

Це виведе:

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

або

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

дякую, скопіювавши цей код, спочатку скопіюйте у цьому проекті, над яким я працюю, написавши це, здавалося, нудне завдання :)
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);
        }
}

повинен працювати - неперевірений.


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
Оплата витрат за виправлення зашкодить роботі. Передача відповідальності за знищення буфера абоненту недобре.
EvilTeach

2

Далі покажемо макет пам'яті:

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

Що ви маєте на увазі під «Далі покаже вам макет пам'яті» ?
Пітер Мортенсен

2

Ось невелика варіація рішення paniq , яка використовує шаблони для друку 32 та 64 бітових цілих чисел:

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

І може використовуватися так:

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

Ось результат:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.