Badr
Badr

Reputation: 10668

How do I get bit-by-bit data from an integer value in C?

I want to extract bits of a decimal number.

For example, 7 is binary 0111, and I want to get 0 1 1 1 all bits stored in bool. How can I do so?

OK, a loop is not a good option, can I do something else for this?

Upvotes: 134

Views: 262877

Answers (9)

Toby Speight
Toby Speight

Reputation: 30968

You say that you don't want a loop. However, you can write a loop in your source, and a decent compiler will be able to unroll that loop when the number of bits to convert is fixed.

For example, consider this conversion function:

/* Store the COUNT least-signfificant bits of VALUE into DEST */
void to_bits(unsigned value, int count, bool dest[count])
{
    bool *d = dest;
    while (count--) {
        *d++ = value & (1u << count);
    }
}

This contains a loop of unknown bound, so will compile to a machine-language loop. But we can show what happens when we ask to convert a specific number of bits, by calling it from another function:

void to_bits_4(unsigned value, bool dest[4])
{
    to_bits(value, 4, dest);
}

If we examine this code on Compiler Explorer, we see that GCC converts it to pure linear code - e.g. for x86:

to_bits_4:
        movq    %rsi, %r8
        movl    %edi, %eax
        movl    %edi, %esi
        movl    %edi, %ecx
        shrl    %esi
        andl    $1, %eax
        shrl    $2, %ecx
        movl    %edi, %edx
        sall    $8, %eax
        andl    $1, %esi
        andl    $1, %ecx
        shrl    $3, %edx
        orl     %esi, %eax
        andl    $1, %edx
        sall    $8, %eax
        orl     %ecx, %eax
        sall    $8, %eax
        orl     %edx, %eax
        movl    %eax, (%r8)
        ret

Note that your compiler is smarter than you are, and is able to use SIMD instructions where available. For example:

void to_bits_16(unsigned value, bool dest[16])
{
    to_bits(value, 16, dest);
}

On x86-64, this becomes:

to_bits_16:
        vmovd   %edi, %xmm6
        vpxor   %xmm3, %xmm3, %xmm3
        vpcmpeqd        %xmm5, %xmm5, %xmm5
        vpbroadcastd    %xmm6, %xmm1
        vpsrld  $31, %xmm5, %xmm5
        vpxor   %xmm4, %xmm4, %xmm4
        vpand   .LC0(%rip), %xmm1, %xmm0
        vpand   .LC2(%rip), %xmm1, %xmm2
        vpcmpeqd        %xmm3, %xmm0, %xmm0
        vpcmpeqd        %xmm3, %xmm2, %xmm2
        vpcmpeqd        %xmm3, %xmm0, %xmm0
        vpcmpeqd        %xmm3, %xmm2, %xmm2
        vpand   %xmm5, %xmm0, %xmm0
        vpand   %xmm5, %xmm2, %xmm2
        vpblendw        $85, %xmm2, %xmm4, %xmm2
        vpblendw        $85, %xmm0, %xmm4, %xmm0
        vpackusdw       %xmm2, %xmm0, %xmm0
        vpand   .LC3(%rip), %xmm1, %xmm2
        vpand   .LC4(%rip), %xmm1, %xmm1
        vpcmpeqd        %xmm3, %xmm2, %xmm2
        vpcmpeqd        %xmm3, %xmm1, %xmm1
        vpcmpeqd        %xmm3, %xmm2, %xmm2
        vpcmpeqd        %xmm3, %xmm1, %xmm1
        vpand   %xmm5, %xmm2, %xmm2
        vpand   %xmm5, %xmm1, %xmm1
        vpblendw        $85, %xmm2, %xmm4, %xmm2
        vpblendw        $85, %xmm1, %xmm4, %xmm4
        vpackusdw       %xmm4, %xmm2, %xmm1
        vpcmpeqd        %xmm2, %xmm2, %xmm2
        vpsrlw  $8, %xmm2, %xmm2
        vpand   %xmm0, %xmm2, %xmm0
        vpand   %xmm1, %xmm2, %xmm2
        vpackuswb       %xmm2, %xmm0, %xmm0
        vmovdqu %xmm0, (%rsi)
        ret
.LC0:
        .long   32768
        .long   16384
        .long   8192
        .long   4096
.LC2:
        .long   2048
        .long   1024
        .long   512
        .long   256
.LC3:
        .long   128
        .long   64
        .long   32
        .long   16
.LC4:
        .long   8
        .long   4
        .long   2
        .long   1

And on AArch64:

to_bits_16:
        dup     v31.4s, w0
        adrp    x0, .LC0
        ldr     q0, [x0, #:lo12:.LC0]
        adrp    x0, .LC1
        ldr     q29, [x0, #:lo12:.LC1]
        adrp    x0, .LC2
        cmtst   v0.4s, v31.4s, v0.4s
        ldr     q28, [x0, #:lo12:.LC2]
        adrp    x0, .LC3
        cmtst   v29.4s, v31.4s, v29.4s
        ldr     q30, [x0, #:lo12:.LC3]
        cmtst   v28.4s, v31.4s, v28.4s
        and     z0.s, z0.s, 1
        cmtst   v30.4s, v31.4s, v30.4s
        and     z29.s, z29.s, 1
        and     z28.s, z28.s, 1
        and     z30.s, z30.s, 1
        uzp1    v29.8h, v0.8h, v29.8h
        uzp1    v30.8h, v28.8h, v30.8h
        uzp1    v30.16b, v29.16b, v30.16b
        str     q30, [x1]
        ret
.LC0:
        .word   32768
        .word   16384
        .word   8192
        .word   4096
.LC1:
        .word   2048
        .word   1024
        .word   512
        .word   256
.LC2:
        .word   128
        .word   64
        .word   32
        .word   16
.LC3:
        .word   8
        .word   4
        .word   2
        .word   1

Upvotes: 0

Joe
Joe

Reputation: 6777

As requested, I decided to extend my comment on forefinger's answer to a full-fledged answer. Although his answer is correct, it is needlessly complex. Furthermore all current answers use signed ints to represent the values. This is dangerous, as right-shifting of negative values is implementation-defined (i.e. not portable) and left-shifting can lead to undefined behavior (see this question).

By right-shifting the desired bit into the least significant bit position, masking can be done with 1. No need to compute a new mask value for each bit.

(n >> k) & 1

As a complete program, computing (and subsequently printing) an array of single bit values:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    unsigned
        input = 0b0111u,
        n_bits = 4u,
        *bits = malloc(sizeof *bits * n_bits),
        bit = 0;

    if (!bits) { return EXIT_FAILURE; }

    for (bit = 0;  bit < n_bits;  ++bit) {
        bits[bit] = (input >> bit) & 1;
    }

    for (bit = n_bits;  bit--;  ) {
        printf("%u", bits[bit]);
    }
    printf("\n");

    free(bits);
}

Assuming that you want to calculate all bits as in this case, and not a specific one, the loop can be further changed to

for(bit = 0;  bit < n_bits;  ++bit, input >>= 1) {
    bits[bit] = input & 1;
}

This modifies input in place and thereby allows the use of a constant width, single-bit shift, which may be more efficient on some architectures.

Upvotes: 109

user806168
user806168

Reputation: 477

If I could rephrase the question, a more general request to extract a set of bits from an integer could be stated as:

I want to extract "size" number of bits from a decimal number "n" from "pos" bit onwards so that I get the bits starting from bit positions "pos + size" to "pos"

The more general statement above can then be stated as:

I want to extract 1 bit from a decimal number "n' from bit position "pos"

You can then change the above statement to:

I want to extract the bits of a from a decimal number "n" one bit at a time and store it into a boolean array so that the bit position "pos" corresponds to the array index "pos".

Assuming that the first "position" of the bit in the decimal number is the LSB i.e bit 0, we can extract n bits from a decimal number like this:

create mask for number of bits : (1 << size) - 1
shift the number by number of bits so that the bit at index is now the LSB : n >> pos
Logical AND the shifted value with the mask : (n >> pos) & ((1 << size) - 1)

Here is an example:

#define GET_N_BITS(n, size, pos, ret) ({ \
        ret = (n >> pos) & ((1 << size) - 1); \
)}

main()
{
        int n = 7;
        int extracted_bits = 0;
        // extract 2 bits from bit 1 onwards from decimal number 7
        GET_N_BITS(n, 2, 1, extracted_bits);
        // extracted bits = b0011 = 3
}

We can then change the above to extract and return one bit from a given bit position and store it in an array.

#define GET_NTH_BIT(n, pos) ({ \
        bool ret; \
        ret = (n >> pos) & 1 \
        ret; \
})

main()
{
        int n = 7, i = 0;
        bool n_bit_by_bit[32];
        for (i = 0 ; i < 32 ; i++)
                n_bit_by_bit[i] = GET_NTH_BIT(n, i);
}

Upvotes: 0

wallyk
wallyk

Reputation: 57794

Here's one way to do it—there are many others:

bool b[4];
int v = 7;  // number to dissect

for (int j = 0;  j < 4;  ++j)
   b [j] =  0 != (v & (1 << j));

It is hard to understand why use of a loop is not desired, but it is easy enough to unroll the loop:

bool b[4];
int v = 7;  // number to dissect

b [0] =  0 != (v & (1 << 0));
b [1] =  0 != (v & (1 << 1));
b [2] =  0 != (v & (1 << 2));
b [3] =  0 != (v & (1 << 3));

Or evaluating constant expressions in the last four statements:

b [0] =  0 != (v & 1);
b [1] =  0 != (v & 2);
b [2] =  0 != (v & 4);
b [3] =  0 != (v & 8);

Upvotes: 15

forefinger
forefinger

Reputation: 3867

If you want the k-th bit of n, then do

(n & ( 1 << k )) >> k

Here we create a mask, apply the mask to n, and then right shift the masked value to get just the bit we want. We could write it out more fully as:

    int mask =  1 << k;
    int masked_n = n & mask;
    int thebit = masked_n >> k;

You can read more about bit-masking here.

Here is a program:

#include <stdio.h>
#include <stdlib.h>

int *get_bits(int n, int bitswanted){
  int *bits = malloc(sizeof(int) * bitswanted);

  int k;
  for(k=0; k<bitswanted; k++){
    int mask =  1 << k;
    int masked_n = n & mask;
    int thebit = masked_n >> k;
    bits[k] = thebit;
  }

  return bits;
}

int main(){
  int n=7;

  int  bitswanted = 5;

  int *bits = get_bits(n, bitswanted);

  printf("%d = ", n);

  int i;
  for(i=bitswanted-1; i>=0;i--){
    printf("%d ", bits[i]);
  }

  printf("\n");
}

Upvotes: 209

wildplasser
wildplasser

Reputation: 44250

If you don't want any loops, you'll have to write it out:

#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    int num = 7;

    #if 0
        bool arr[4] = { (num&1) ?true: false, (num&2) ?true: false, (num&4) ?true: false, (num&8) ?true: false };
    #else
        #define BTB(v,i) ((v) & (1u << (i))) ? true : false
        bool arr[4] = { BTB(num,0), BTB(num,1), BTB(num,2), BTB(num,3)};
        #undef BTB
    #endif

    printf("%d %d %d %d\n", arr[3], arr[2], arr[1], arr[0]);

    return 0;
}

As demonstrated here, this also works in an initializer.

Upvotes: 1

d3vdpro
d3vdpro

Reputation: 3007

Here's a very simple way to do it;

int main()
{
    int s=7,l=1;
    vector <bool> v;
    v.clear();
    while (l <= 4)
    {
        v.push_back(s%2);
        s /= 2;
        l++;
    }
    for (l=(v.size()-1); l >= 0; l--)
    {
        cout<<v[l]<<" ";
    }
    return 0;
}

Upvotes: 3

xinthose
xinthose

Reputation: 3848

@prateek thank you for your help. I rewrote the function with comments for use in a program. Increase 8 for more bits (up to 32 for an integer).

std::vector <bool> bits_from_int (int integer)    // discern which bits of PLC codes are true
{
    std::vector <bool> bool_bits;

    // continously divide the integer by 2, if there is no remainder, the bit is 1, else it's 0
    for (int i = 0; i < 8; i++)
    {
        bool_bits.push_back (integer%2);    // remainder of dividing by 2
        integer /= 2;    // integer equals itself divided by 2
    }

    return bool_bits;
}

Upvotes: 1

anon
anon

Reputation: 1

#include <stdio.h>

int main(void)
{
    int number = 7; /* signed */
    int vbool[8 * sizeof(int)];
    int i;
        for (i = 0; i < 8 * sizeof(int); i++)
        {
            vbool[i] = number<<i < 0;   
            printf("%d", vbool[i]);
        }
    return 0;
}

Upvotes: 0

Related Questions