Có một công cụ chuyển đổi printf để in ở định dạng nhị phân?


433

Tôi có thể in với printf dưới dạng số hex hoặc bát phân. Có một thẻ định dạng để in dưới dạng nhị phân, hoặc cơ sở tùy ý?

Tôi đang chạy gcc.

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

Bạn không thể làm điều này, theo như tôi biết, sử dụng printf. Rõ ràng, bạn có thể viết một phương thức trợ giúp để thực hiện điều này, nhưng điều đó không giống như hướng bạn muốn đi.
Ian P

Không có một định dạng được xác định trước cho điều đó. Bạn cần phải tự chuyển đổi nó thành một chuỗi và sau đó in chuỗi.
rslite

Một tìm kiếm nhanh của Google đã tạo ra trang này với một số thông tin có thể hữu ích: forum.macrumors.com/archive/index.php/t-165959.html
Ian P

12
Không phải là một phần của Thư viện C Tiêu chuẩn ANSI - nếu bạn đang viết mã di động, phương pháp an toàn nhất là tự cuộn.
tomlogic

Một tiêu chuẩn và chung chung (đối với bất kỳ loại Tích phân nào có độ dài bất kỳ) của chuyển đổi thành chuỗi nhị phân trên C ++: stackoverflow.com/a/31660310/1814353
luart

Câu trả lời:


266

Hacky nhưng làm việc cho tôi:

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

Đối với các loại nhiều byte

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

Bạn cần tất cả các báo giá thêm không may. Cách tiếp cận này có rủi ro hiệu quả của các macro (không chuyển một hàm làm đối số BYTE_TO_BINARY) nhưng tránh các vấn đề về bộ nhớ và nhiều yêu cầu của strcat trong một số đề xuất khác ở đây.


13
Và cũng có lợi thế là có thể vô hình nhiều lần trong printfđó những người có staticbộ đệm không thể.
Patrick Schlüter

4
Tôi đã có quyền tự do thay đổi %dthành %c, bởi vì nó thậm chí còn nhanh hơn ( %dphải thực hiện chuyển đổi chữ số-> char, trong khi %cchỉ cần đưa ra đối số

3
Đã đăng phiên bản mở rộng của macro này với hỗ trợ int 16, 32, 64 bit: stackoverflow.com/a/25108449/432509
ideaman42

2
Lưu ý rằng phương pháp này không thân thiện với chồng. Giả sử intlà 32 bit trên hệ thống, in một giá trị 32 bit sẽ yêu cầu không gian cho các giá trị 32 * 4 byte; tổng cộng 128 byte. Mà, tùy thuộc vào kích thước ngăn xếp, có thể hoặc không thể là một vấn đề.
dùng694733

1
điều quan trọng là thêm dấu ngoặc quanh byte trong macro hoặc bạn có thể gặp sự cố khi gửi thao tác BYTE_TO_BINARY (a | b) -> a | b & 0x01! = (a | b) & 0x01
Ivan Hoffmann

203

In nhị phân cho bất kỳ kiểu dữ liệu nào

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

kiểm tra

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; )nghị để tránh size_tso với inttrận đấu sai.
chux - Phục hồi Monica

1
Ai đó có thể vui lòng giải thích về logic đằng sau mã này?
jII

2
Lấy từng byte trong ptr(vòng ngoài); sau đó với mỗi bit byte hiện tại (vòng lặp bên trong), che dấu byte bằng bit hiện tại ( 1 << j). Thay đổi quyền đó dẫn đến một byte chứa 0 ( 0000 0000b) hoặc 1 ( 0000 0001b). In ra byte printf với định dạng %u. HTH.
nielsbot 17/2/2016

1
@ ZX9 Lưu ý rằng được đề xuất được sử dụng >cùng với size_tkhông phải là >=nhận xét của bạn để xác định thời điểm chấm dứt vòng lặp.
chux - Phục hồi Monica

3
@ ZX9 Vẫn là một nhận xét ban đầu hữu ích của bạn vì các lập trình viên cần phải cẩn thận xem xét việc sử dụng trường hợp cạnh >>=với các loại không dấu. 0là một trường hợp cạnh không dấu và thường xảy ra, không giống như toán học đã ký với ít phổ biến hơn INT_MAX/INT_MIN.
chux - Phục hồi Monica

151

Dưới đây là một bản hack nhanh để thể hiện các kỹ thuật để làm những gì bạn muốn.

#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
Điều này chắc chắn ít "lạ" hơn so với việc viết tùy chỉnh quá tải thoát cho printf. Thật đơn giản để hiểu cho một nhà phát triển mới đối với mã.
tức giận

43
Một vài thay đổi: strcatlà một phương pháp không hiệu quả khi thêm một char vào chuỗi trên mỗi lần vượt qua của vòng lặp. Thay vào đó, thêm một char *p = b;và thay thế vòng lặp bên trong bằng *p++ = (x & z) ? '1' : '0'. znên bắt đầu từ 128 (2 ^ 7) thay vì 256 (2 ^ 8). Xem xét cập nhật để đưa một con trỏ đến bộ đệm để sử dụng (để đảm bảo an toàn cho luồng), tương tự như inet_ntoa().
tomlogic

3
@EvilTeach: Chính bạn đang sử dụng toán tử ternary làm tham số strcat()! Tôi đồng ý rằng điều đó strcatcó thể dễ hiểu hơn so với việc tăng sau một con trỏ được bỏ qua cho bài tập, nhưng ngay cả những người mới bắt đầu cũng cần biết cách sử dụng đúng thư viện chuẩn. Có thể sử dụng một mảng được lập chỉ mục để gán sẽ là một minh chứng tốt (và thực sự sẽ hoạt động, vì bkhông được đặt lại thành các số không mỗi khi bạn gọi hàm).
tomlogic

3
Ngẫu nhiên: char bộ đệm nhị phân là tĩnh và được xóa cho tất cả các số không trong bài tập. Điều này sẽ chỉ xóa nó lần đầu tiên khi nó chạy và sau đó nó sẽ không xóa, mà thay vào đó sử dụng giá trị cuối cùng.
markwatson

8
Ngoài ra, điều này sẽ ghi lại rằng kết quả trước đó sẽ không hợp lệ sau khi gọi lại hàm, vì vậy người gọi không nên cố gắng sử dụng nó như thế này : printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann

84

Bình thường không có công cụ chuyển đổi nhị phân trong glibc.

Có thể thêm các loại chuyển đổi tùy chỉnh vào họ hàm printf () trong glibc. Xem register_printf_function để biết chi tiết. Bạn có thể thêm một chuyển đổi% b tùy chỉnh cho mục đích sử dụng của riêng bạn, nếu nó đơn giản hóa mã ứng dụng để có sẵn nó.

Dưới đây là một ví dụ về cách triển khai định dạng printf tùy chỉnh trong glibc.


Tôi đã luôn tự viết v [snf] printf () cho các trường hợp giới hạn mà tôi muốn các cơ số khác nhau: rất vui vì tôi đã duyệt qua điều này.
Jamie

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Có một chức năng mới để làm tương tự, mặc dù : register_printf_specifier(). Một ví dụ về cách sử dụng mới có thể được tìm thấy ở đây: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito

47

Bạn có thể sử dụng một bảng nhỏ để cải thiện tốc độ 1 . Các kỹ thuật tương tự rất hữu ích trong thế giới nhúng, ví dụ, để đảo ngược một 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 Tôi chủ yếu đề cập đến các ứng dụng nhúng trong đó trình tối ưu hóa không quá mạnh và có thể thấy sự khác biệt về tốc độ.


27

In bit quan trọng nhất và dịch chuyển nó ra bên phải. Làm điều này cho đến khi số nguyên trở thành số không in biểu diễn nhị phân mà không có số 0 đứng đầu nhưng theo thứ tự đảo ngược. Sử dụng đệ quy, thứ tự có thể được sửa chữa khá dễ dàng.

#include <stdio.h>

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

Đối với tôi, đây là một trong những giải pháp sạch nhất cho vấn đề. Nếu bạn thích 0btiền tố và một ký tự dòng mới, tôi khuyên bạn nên bọc hàm.

Bản demo trực tuyến


lỗi: quá ít đối số cho chức năng gọi, dự kiến ​​2, có 1 putc ((số & 1)? '1': '0');
Koray Tugay

@KorayTugay Cảm ơn bạn đã chỉ ra điều đó. Tôi đã sửa lỗi gọi hàm và thêm một bản demo.
danijar

6
bạn cũng nên sử dụng số int không dấu, vì khi số đã cho là âm, hàm sẽ nhập vào một cuộc gọi đệ quy không bao giờ kết thúc.
Puffy

Cách tiếp cận hiệu quả hơn, vì trong ASCII, '0' + 1 = '1':putc('0'+(number&1), stdout);
Roger Dueck

22

Dựa trên câu trả lời @William Whyte, đây là một vĩ mô cung cấp int8, 16, 32& 64các phiên bản, tái sử dụng các INT8macro để tránh lặp lại.

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

Kết quả này:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Để dễ đọc, bạn có thể muốn thêm một dấu phân cách, ví dụ:

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

Thật tuyệt vời. Có một lý do cụ thể nào cho việc in các bit bắt đầu bằng Least Signanticant Bits không?
gaganso

2
Làm thế nào bạn sẽ đề nghị thêm dấu phẩy?
nmz787

Sẽ thêm một phiên bản PRINTF_BYTE_TO_BINARY_INT#định nghĩa được nhóm lại để sử dụng tùy ý.
ideaman42

16

Đây là phiên bản của hàm không gặp phải vấn đề giới hạn hoặc giới hạn về kích thước / loại đối số:

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

Lưu ý rằng mã này sẽ hoạt động tốt như nhau đối với bất kỳ cơ sở nào trong khoảng từ 2 đến 10 nếu bạn chỉ thay thế 2 cơ sở bằng cơ sở mong muốn. Cách sử dụng là:

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

Trường hợp xbất kỳ biểu thức tích phân.


7
Vâng, bạn có thể làm điều đó. Nhưng đó là thiết kế thực sự xấu. Ngay cả khi bạn không có chủ đề hoặc reentrancy, người gọi phải nhận thức được rằng bộ đệm tĩnh đang được sử dụng lại và những thứ như char *a = binary_fmt(x), *b = binary_fmt(y);sẽ không hoạt động như mong đợi. Việc buộc người gọi phải vượt qua một bộ đệm làm cho yêu cầu lưu trữ bùng nổ; người gọi tất nhiên là miễn phí sử dụng bộ đệm tĩnh nếu điều đó thực sự mong muốn, và sau đó việc sử dụng lại bộ đệm đó trở nên rõ ràng. Cũng lưu ý rằng, trên các ABI PIC hiện đại, bộ đệm tĩnh thường tốn nhiều mã để truy cập hơn bộ đệm trên ngăn xếp.
R .. GitHub DỪNG GIÚP ICE

8
Đó vẫn là một thiết kế tồi. Nó đòi hỏi một bước sao chép thêm trong những trường hợp đó và nó không tốn kém hơn việc người gọi cung cấp bộ đệm ngay cả trong trường hợp không cần sao chép. Sử dụng lưu trữ tĩnh chỉ là một thành ngữ xấu.
R .. GitHub DỪNG GIÚP ICE

3
Phải làm ô nhiễm không gian tên của bảng tiền xử lý hoặc biểu tượng biến với một tên phụ không cần thiết phải được sử dụng để kích thước đúng dung lượng lưu trữ phải được phân bổ bởi mọi người gọi và buộc mọi người gọi phải biết giá trị này và phân bổ số lượng cần thiết lưu trữ, là thiết kế xấu khi giải pháp lưu trữ cục bộ chức năng đơn giản hơn sẽ đủ cho hầu hết các ý định và mục đích, và khi một lệnh gọi strdup () đơn giản chiếm 99% phần còn lại của việc sử dụng.
Greg A. Woods

5
Ở đây chúng tôi sẽ phải không đồng ý. Tôi không thể thấy việc thêm một biểu tượng tiền xử lý không phô trương đến bất kỳ nơi nào gần với tác hại của việc hạn chế các trường hợp sử dụng nghiêm trọng, khiến giao diện dễ bị lỗi, dự trữ bộ nhớ vĩnh viễn trong thời gian của chương trình cho giá trị tạm thời và tạo ra mã kém hơn trên hầu hết nền tảng hiện đại.
R .. GitHub DỪNG GIÚP ICE

5
Tôi không ủng hộ việc tối ưu hóa vi mô mà không có lý do (tức là các phép đo). Nhưng tôi nghĩ rằng hiệu suất, ngay cả khi nó ở mức tăng vi mô, đáng được đề cập khi nó là một phần thưởng cùng với một thiết kế vượt trội về cơ bản.
R .. GitHub DỪNG GIÚP ICE

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
Rõ ràng hơn nếu bạn sử dụng '1''0'thay vì 4948trong ternary của bạn. Ngoài ra, bnên dài 9 ký tự để ký tự cuối cùng có thể vẫn là dấu kết thúc null.
tomlogic

Ngoài ra B cần phải được khởi tạo mỗi lần.
EvilTeach

2
Không phải nếu bạn thay đổi một số: 1. thêm khoảng trắng cho số 0 cuối cùng: static char b[9] = {0}2. di chuyển khai báo ra khỏi vòng lặp: int z,y;3. Thêm số không cuối cùng : b[y] = 0. Cách này không cần tái sinh.
Kobor42

1
Giải pháp tốt đẹp. Tôi sẽ thay đổi một số thứ mặc dù. Tức là đi lùi trong chuỗi để đầu vào có kích thước bất kỳ có thể được xử lý đúng.
Kobor42

Tất cả những cái đó 8nên được thay thế bởi CHAR_BIT.
kiềm

12

Giải pháp nhanh chóng và dễ dàng:

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

Hoạt động cho bất kỳ loại kích thước và cho ints đã ký và không dấu. '& 1' là cần thiết để xử lý các số nguyên đã ký vì ca làm việc có thể thực hiện gia hạn ký.

Có rất nhiều cách để làm điều này. Đây là một cách cực kỳ đơn giản để in 32 bit hoặc n bit từ loại 32 bit đã ký hoặc không dấu (không đặt âm nếu được ký, chỉ in các bit thực tế) và không trả về vận chuyển. Lưu ý rằng tôi bị giảm trước khi dịch chuyển 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)

Điều gì về việc trả về một chuỗi với các bit để lưu trữ hoặc in sau này? Bạn có thể phân bổ bộ nhớ và trả lại và người dùng phải giải phóng nó, nếu không bạn trả lại một chuỗi tĩnh nhưng nó sẽ bị ghi đè nếu nó được gọi lại hoặc bởi một luồng khác. Cả hai phương pháp được hiển thị:

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

Gọi với:

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

Không có câu trả lời nào được đăng trước đó chính xác là những gì tôi đang tìm kiếm, vì vậy tôi đã viết một câu trả lời. Thật đơn giản để sử dụng% B với 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
điều này sẽ tràn hơn 50 bit?
Janus Troelsen

Cuộc gọi tốt, vâng nó sẽ ... Tôi đã nói rằng tôi cần sử dụng malloc, bao giờ không?
TechplexEngineer

Phải, tất nhiên. siêu dễ:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen

@JanusTroelsen, hoặc sạch hơn, nhỏ hơn, có thể bảo trì:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz

Tại sao "% B" lại khác với "% b" về mặt này? Các câu trả lời trước đây đã nói những điều như "Không có chức năng định dạng trong thư viện chuẩn C để xuất nhị phân như thế." và " Một số thời gian chạy hỗ trợ"% b "mặc dù đó không phải là tiêu chuẩn." .
Peter Mortensen

8

Một số thời gian chạy hỗ trợ "% b" mặc dù đó không phải là một tiêu chuẩn.

Cũng xem ở đây cho một cuộc thảo luận thú vị:

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

HTH


1
Đây thực sự là một thuộc tính của thư viện thời gian chạy C, không phải trình biên dịch.
cjm

7

Mã này sẽ xử lý nhu cầu của bạn lên đến 64 bit. Tôi đã tạo 2 hàm pBin & pBinFill. Cả hai đều làm điều tương tự, nhưng pBinFill lấp đầy trong các không gian hàng đầu với fillChar. Hàm kiểm tra tạo ra một số dữ liệu kiểm tra, sau đó in ra bằng cách sử dụng chức năng.



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" xung đột với stream.h từ log4cxx. Vui lòng sử dụng tên xác định ngẫu nhiên theo quy ước :)
kagali-san

5
@mhambra: bạn nên tắt log4cxx vì sử dụng tên chung như vậy widththay vào đó!
u0b34a0f6ae

7

Có một công cụ chuyển đổi printf để in ở định dạng nhị phân?

Các printf()gia đình duy nhất có khả năng in trong cơ sở 8, 10, và 16 bằng cách sử dụng tiêu chuẩn specifiers trực tiếp. Tôi đề nghị tạo một hàm chuyển đổi số thành chuỗi theo nhu cầu cụ thể của mã.


Để in trong bất kỳ cơ sở nào [2-36]

Tất cả các câu trả lời khác cho đến nay có ít nhất một trong những hạn chế này.

  1. Sử dụng bộ nhớ tĩnh cho bộ đệm trở lại. Điều này giới hạn số lần hàm có thể được sử dụng làm đối số printf().

  2. Phân bổ bộ nhớ yêu cầu mã gọi đến con trỏ miễn phí.

  3. Yêu cầu mã gọi để cung cấp một bộ đệm phù hợp.

  4. Gọi printf()trực tiếp. Này buộc một chức năng mới cho tới fprintf(), sprintf(), vsprintf()vv

  5. Sử dụng một phạm vi số nguyên giảm.

Sau đây không có giới hạn ở trên . Nó không yêu cầu C99 trở lên và sử dụng "%s". Nó sử dụng một nghĩa đen để cung cấp không gian bộ đệm. Nó không gặp rắc rối với nhiều cuộc gọi trong 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;
}

Đầu ra

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

Điều này rất hữu ích. Bạn có biết làm thế nào để sử dụng nó trong C ++? Khi tôi biên dịch, nó sẽ phát sinh lỗi "Mô tả mã nghiêm trọng Mô tả dự án Dòng trạng thái loại bỏ lỗi C4576 một loại dấu ngoặc đơn theo sau là danh sách trình khởi tạo là cú pháp chuyển đổi loại rõ ràng không chuẩn, xin chào C: \ my_projects \ hello \ hello \ main.cpp 39 "
Chỉ là người học

1
@Justalearner Điều này tạo ra C ++ bởi vì nếu sử dụng một tính năng ghép chữ C không phải là một phần của C ++. Có lẽ đăng bài triển khai C ++ của bạn để cố gắng thực hiện tương tự - ngay cả khi không đầy đủ, tôi chắc chắn bạn sẽ nhận được trợ giúp - miễn là bạn thể hiện nỗ lực của mình trước.
chux - Tái lập lại

6

Có thể một chút OT, nhưng nếu bạn chỉ cần điều này để gỡ lỗi để hiểu hoặc truy xuất một số thao tác nhị phân bạn đang thực hiện, bạn có thể xem qua wcalc (một máy tính bảng điều khiển đơn giản). Với các tùy chọn -b, bạn có được đầu ra nhị phân.

ví dụ

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

1
cũng có một vài lựa chọn khác ở mặt trước này ... ruby -e 'printf("%b\n", 0xabc)', dctiếp 2otheo là tiếp theo 0x123p, v.v.
lindes

6

Không có chức năng định dạng trong thư viện chuẩn C để xuất nhị phân như thế. Tất cả các hoạt động định dạng mà gia đình printf hỗ trợ đều hướng tới văn bản có thể đọc được của con người.


5

Hàm đệ quy sau có thể hữu ích:

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

7
Hãy cẩn thận, điều này không hoạt động với số nguyên âm.
Anderson Freitas

4

Tôi đã tối ưu hóa giải pháp hàng đầu cho kích thước và C ++ - và đã có giải pháp này:

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
Nếu bạn muốn sử dụng bộ nhớ động (thông qua std::string), bạn cũng có thể thoát khỏi staticmảng. Cách đơn giản nhất là chỉ cần bỏ staticvòng loại và tạo bcục bộ cho hàm.
Shahbaz

((x>>z) & 0x01) + '0'là đủ.
Jason C

4

In bit từ bất kỳ loại nào sử dụng ít mã và tài nguyên

Cách tiếp cận này có các thuộc tính:

  • Hoạt động với các biến và nghĩa đen.
  • Không lặp lại tất cả các bit khi không cần thiết.
  • Chỉ gọi printf khi hoàn thành một byte (không cần thiết cho tất cả các bit).
  • Hoạt động cho bất kỳ loại.
  • Hoạt động với độ bền nhỏ và lớn (sử dụng GCC #defines để kiểm tra).
  • Sử dụng typeof () không phải là tiêu chuẩn C nhưng phần lớn được xác định.
#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;
}

Đầu ra

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

Tôi đã sử dụng một cách tiếp cận khác ( bitprint.h ) để điền vào một bảng với tất cả các byte (dưới dạng chuỗi bit) và in chúng dựa trên byte đầu vào / chỉ mục. Thật đáng để xem.


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

}

Hoặc thêm 0 hoặc 1 vào giá trị ký tự của '0';) Không cần bộ ba.

3

Tôi thích mã bởi paniq, bộ đệm tĩnh là một ý tưởng tốt. Tuy nhiên, nó không thành công nếu bạn muốn nhiều định dạng nhị phân trong một printf () vì nó luôn trả về cùng một con trỏ và ghi đè lên mảng.

Đây là một kiểu thả xuống kiểu C xoay con trỏ trên bộ đệm chia.

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
Khi countđạt đến MAXCNT - 1, mức tăng tiếp theo countsẽ làm cho nó MAXCNTthay vì bằng 0, điều này sẽ khiến quyền truy cập ra khỏi ranh giới của mảng. Bạn nên làm count = (count + 1) % MAXCNT.
Shahbaz

1
Nhân tiện, điều này sẽ gây bất ngờ cho một nhà phát triển sử dụng MAXCNT + 1các cuộc gọi đến chức năng này trong một lần duy nhất printf. Nói chung, nếu bạn muốn cung cấp tùy chọn cho nhiều hơn 1 điều, hãy làm cho nó vô hạn. Các số như 4 chỉ có thể gây ra vấn đề.
Shahbaz

3

Không có cách tiêu chuẩn và di động.

Một số triển khai cung cấp itoa () , nhưng nó sẽ không xuất hiện nhiều nhất và nó có giao diện hơi nhàu nát. Nhưng mã nằm phía sau liên kết và sẽ cho phép bạn thực hiện trình định dạng của riêng bạn khá dễ dàng.


3

Một câu lệnh chuyển đổi chung của bất kỳ loại tích phân nào thành biểu diễn chuỗi nhị phân bằng thư viện chuẩn :

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

Hoặc chỉ: std::cout << std::bitset<sizeof(num) * 8>(num);


1
Đó là một giải pháp thành ngữ cho C ++ nhưng anh ấy đã yêu cầu C.
danijar 7/10/2015

3

Giải pháp của tôi:

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

Dựa trên đề nghị @ ideasman42 trong câu trả lời của mình, đây là một vĩ mô cung cấp int8, 16, 32& 64các phiên bản, tái sử dụng các INT8macro để tránh lặp lại.

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

Kết quả này:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Để dễ đọc, bạn có thể thay đổi: #define PRINTF_BINARY_SEPARATORthành #define PRINTF_BINARY_SEPARATOR ","hoặc#define PRINTF_BINARY_SEPARATOR " "

Điều này sẽ xuất ra:

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

hoặc là

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

cảm ơn sao chép mã này, lần đầu tiên để sao chép trong dự án này tôi đang làm việc, viết nó cảm thấy như một nhiệm vụ tẻ nhạt :)
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);
        }
}

nên làm việc - chưa được kiểm tra.


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
Trả chi phí cho một khu mua sắm sẽ làm giảm hiệu suất. Truyền trách nhiệm cho việc phá hủy bộ đệm cho người gọi là không tốt.
EvilTeach

2

Tiếp theo sẽ hiển thị cho bạn bố trí bộ nhớ:

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

Bạn có ý nghĩa gì bởi "Tiếp theo sẽ hiển thị cho bạn bố cục bộ nhớ" ?
Peter Mortensen

2

Đây là một biến thể nhỏ của giải pháp paniq sử dụng các mẫu để cho phép in các số nguyên 32 và 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);
}

Và có thể được sử dụng như:

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

Đây là kết quả:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.