Mask and extract bits in C
Asked Answered
F

7

5

I've been looking at posts about masks, but I still can't get my head around how to extract certain bits from a number in C.

Say if we have an integer number, 0001 1010 0100 1011, its hexadecimal representation is 0x1A4B, right? If I want to know the 5th to 7th number, which is 101 in this case, shall I use int mask= 0x0000 1110 0000 0000, int extract = mask&number?

Also, how can I check if it is 101? I guess == won't work here...

Fabliau answered 14/10, 2014 at 10:54 Comment(4)
if you wanna check the 5th and 7th number is 101 or not, then keep mask as int mask= 0x0000 1010 0000 0000. and then ExNOR it.. if extract 5th and 7th bit is 111, then it is 101Jut
What is this 0x0000 1110 0000? In case you want to show a bit series this is wrong, as 0x indicates a hexa-decimal literal.Sanitize
Some canonical questions are What is bit masking? and How do I set, clear, and toggle a single bit?.Nevillenevin
And for setting bits: How do you set only certain bits of a byte in C without affecting the rest? and How to replace bits in a bitfield without affecting other bits using C.Nevillenevin
S
5

Assuming the GCC extension 0b to define binary literals:

int number = 0b0001101001001011; /* 0x1A4B */
int mask =   0b0000111000000000; /* 0x0E00 */
/* &'ed:     0b0000101000000000;    0x0A00 */
int extract = mask & number;     /* 0x0A00 */

if (extract == 0b0000101000000000)
/* Or if 0b is not available:
if (extract == 0x0a00 ) */
{
  /* Success */
}
else
{
  /* Failure */
}
Sanitize answered 14/10, 2014 at 11:25 Comment(1)
Slightly related: Is there a way to do grouping in GCC binary literals?Nevillenevin
R
6

Masking is done by setting all the bits except the one(s) you want to 0. So let's say you have a 8 bit variable and you want to check if the 5th bit from the is a 1. Let's say your variable is 00101100. To mask all the other bits we set all the bits except the 5th one to 0 using the & operator:

00101100 & 00010000

Now what this does is for every bit except the 5th one, the bit from the byte on the right will be 0, so the result of the & operation will be 0. For the 5th bit, however, the value from the right bit is a 1, so the result will be whatever the value of hte 5th bit from the left byte is - in this case 0:

Now to check this value you have to compare it with something. To do this, simply compare the result with the byte on the right:

result = (00101100 & 00010000) == 00000000

To generalize this, you can retrieve any bit from the lefthand byte simply by left-shifting 00000001 until you get the bit you want. The following function achieves this:

int getBit(char byte, int bitNum)
{
    return (byte & (0x1 << (bitNum - 1)))
}

This works on vars of any size, whether it's 8, 16, 32 or 64 (or anything else for that matter).

Rascality answered 14/10, 2014 at 11:9 Comment(0)
S
5

Assuming the GCC extension 0b to define binary literals:

int number = 0b0001101001001011; /* 0x1A4B */
int mask =   0b0000111000000000; /* 0x0E00 */
/* &'ed:     0b0000101000000000;    0x0A00 */
int extract = mask & number;     /* 0x0A00 */

if (extract == 0b0000101000000000)
/* Or if 0b is not available:
if (extract == 0x0a00 ) */
{
  /* Success */
}
else
{
  /* Failure */
}
Sanitize answered 14/10, 2014 at 11:25 Comment(1)
Slightly related: Is there a way to do grouping in GCC binary literals?Nevillenevin
D
1

You need to mask and shift. Either shift the value you are comparing to, or the value you are comparing. I find it easier to think about by shifting the value you are comparing to. So if you're trying to extract the 5th to 7th digits (from the left), you shift right 9 positions (16-7) so that the 7th digit is now the rightmost, then apply 0x7 (111 in binary) as a mask to get only the rightmost three binary digits

int i = 0x1A4B;
if (((i >> 9) & 0x07) == 0x05) { // 0x05 = 101 in binary
    //do what you need to
}
Dichasium answered 14/10, 2014 at 11:6 Comment(3)
you shifted out the relevant bits.Whiny
No I haven't, he's counting from the left, not the right!Dichasium
@vlad_tepesch: How does this not answer the question?Dichasium
B
1

First, the digits in binary are (usually) counted from the right (10th and 12th digit) or you say 5th and 7th most significant digits.

int mask =  0x0E00;  // 0000 1110 0000 0000;
int extract = mask & number;

results in:

extract = 0000 1010 0000 0000

You can do

if (extract == 0x0A00 /*0000 1010 0000 0000*/){}

to test, or:

if (( extract >> 9 ) == 0x05){}

Both of the statements in the if will return true with your sample number.

Usually with a mask you will find yourself testing a single digit. You could use a function like this to test it:

bool digit_value( unsigned int number, unsigned int digit)
{
    return (1 << digit) & number;
}

int main()
{
    unsigned int number = 0x1A4B;
    int should_be_three = 0;
    should_be_three +=  digit_value(number, 10);
    should_be_three += !digit_value(number, 11);
    should_be_three +=  digit_value(number, 12);
    printf("%s", (should_be_three == 3?"it worked":"it didn't work"));
    return 0;
}
Blairblaire answered 14/10, 2014 at 11:12 Comment(3)
Your comarpison is wrong; not trying to compare against hex 0x101, but binary 101, which is 0x05Dichasium
Thanks! but with if (( extract >> 9 ) == 0x101), is x101 actually 0001 0000 0001? Should it be if (( extract >> 9 ) == 0b101)?Fabliau
Yes, I did that a little wrong. Fixed now. (using pure hex)Blairblaire
S
0

shall I use int mask= 0x0000 1110 0000 0000, int extract = mask&number?-

Yes, you can do this.

Also, how can I check if it is 101?

Sure you can check this- 0000 1010 0000 0000 which is 1280 in int.

extract== 1280
Staurolite answered 14/10, 2014 at 11:5 Comment(0)
S
0

It may be simpler to check bits one-by-one, not all at once. At first, you create mask for interested bit:

int fifthBitMask = 1 << 4;
int fifthBitResult = number & fifthBitMask;

int seventhBitMask = 1 << 6;
int seventhBitResult = number & seventhBitMask;

Now, you can compare results with zero OR with mask. Comparing with zero can be omitted, so you can just use simple if:

if (fifthBitResult && seventhBitResult)
{
    //your code here
}

Also, you can compare with masks. After operation &, in result will set only bits, which was set in mask. So, it could like this: if (fifthBitResult == fifthBitMask && seventhBitResult == seventhBitMask) { // your code here }

So, if result of operation is equals to mask, you can do this with one operation:

int mask = 0x5 << 4; // 0x5 is hex representation of 101b
int result = number & mask;
if (result == mask)
{
    // your code here
}
Startle answered 14/10, 2014 at 11:9 Comment(0)
P
0

First of all, your calculation for bits 7-6-5 is incorrect. You stated it was 101, but it is 010 (for x1a43).

Second of all, to get these bits (the value represented by these bits) you should do &0xE0.

int my_bits_from_5to7 = number & 0xE0;

Printer answered 14/10, 2014 at 11:11 Comment(3)
Depends from which end you start counting. Also depends if you are counting from 0 or 1.Pack
@Pack nope, the bits order on most architectures is always the same: from right to left, and the first lowest bit is always number 0.Printer
True, but if the OP is unfamiliar with bitwise operations it is likely that they would also be unaware of the conventions: such as counting from zero and right to left (while English is read from left to right).Pack

© 2022 - 2024 — McMap. All rights reserved.