Reputation: 10668
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
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
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 int
s 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
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
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
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
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
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
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
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