How do I extract bits from 32 bit number

21,053

Solution 1

You need masks to get the bits you want. Masks are numbers that you can use to sift through bits in the manner you want (keep bits, delete/clear bits, modify numbers etc). What you need to know are the AND, OR, XOR, NOT, and shifting operations. For what you need, you'll only need a couple.

You know shifting: x << y moves bits from x *y positions to the left*.

How to get x bits set to 1 in order: (1 << x) - 1

How to get x bits set to 1, in order, starting from y to y + x: ((1 << x) -1) << y

The above is your mask for the bits you need. So for example if you want 16 bits of 0xD7448EAB, from 10 to 25, you'll need the above, for x = 16 and y = 10.

And now to get the bits you want, just AND your number 0xD7448EAB with the mask above and you'll get the masked 0xD7448EAB with only the bits you want. Later, if you want to go through each one, you'll need to shift your result by 10 to the right and process each bit at a time (at position 0).

The answer may be a bit longer, but it's better design than just hard coding with 0xff or whatever.

Solution 2

OK, here's how I wrote it:

#include <stdint.h>
#include <stdio.h>

main() {
    uint32_t in = 0xd7448eab;
    uint16_t out = 0;

    out = in >> 10; // Shift right 10 bits
    out &= 0xffff;  // Only lower 16 bits
    printf("%x\n",out);
}

The in >> 10 shifts the number right 10 bits; the & 0xffff discards all bits except the lower 16 bits.

Solution 3

I combined the top 2 answers above to write a C program that extracts the bits for any range of bits (not just 10 through 25) of a 32-bit unsigned int. The way the function works is that it returns bits lo to hi (inclusive) of num.

#include <stdio.h>
#include <stdint.h>

unsigned extract(unsigned num, unsigned hi, unsigned lo) {
    uint32_t range = (hi - lo + 1);  //number of bits to be extracted
    //shifting a number by the number of bits it has produces inconsistent 
    //results across machines so we need a special case for extract(num, 31, 0)
    if(range == 32)
        return num;
    uint32_t result = 0;
    //following the rule above, ((1 << x) - 1) << y) makes the mask:
    uint32_t mask = ((1 << range) -1) << lo;
    //AND num and mask to get only the bits in our range
    result = num & mask;
    result = result >> lo; //gets rid of trailing 0s
    return result;
}
int main() {
    unsigned int num = 0xd7448eab;
    printf("0x%x\n", extract(num, 10, 25));
}

Solution 4

I want bits 10 through 25.

You can do this:

unsigned int number = 0xD7448EAB;
unsigned int value = (number & 0x3FFFC00) >> 10;

Or this:

unsigned int number = 0xD7448EAB;
unsigned int value = (number >> 10) & 0xFFFF;
Share:
21,053
user3267877
Author by

user3267877

Updated on July 09, 2022

Comments

  • user3267877
    user3267877 almost 2 years

    I have do not have much knowledge of C and I'm stuck with a problem since one of my colleague is on leave.

    I have a 32 bit number and i have to extract bits from it. I did go through a few threads but I'm still not clear how to do so. I would be highly obliged if someone can help me.

    Here is an example of what I need to do:

    Assume hex number = 0xD7448EAB.
    In binary = 1101 0111 0100 0100 1000 1110 1010 1011.
    I need to extract the 16 bits, and output that value. I want bits 10 through 25.

    The lower 10 bits (Decimal) are ignored. i.e., 10 1010 1011 are ignored.
    And the upper 6 bits (Overflow) are ignored. i.e. 1101 01 are ignored.

    The remaining 16 bits of data needs to be the output which is 11 0100 0100 1000 11 (numbers in italics are needed as the output).

    This was an example but I will keep getting different hex numbers all the time and I need to extract the same bits as I explained.

    How do I solve this?
    Thank you.

    For this example you would output 1101 0001 0010 0011, which is 0xD123, or 53,539 decimal.

    • dub stylee
      dub stylee about 10 years
      Do you need to keep everything in binary form? Or would you be able to convert the hex value into binary, then convert the binary value into a string. You could then just take the substring that you are interested in, and convert it back into binary/hexadecimal as needed.
    • 500 - Internal Server Error
      500 - Internal Server Error about 10 years
      And (& operator) your value with a mask with the bits set that you are interested in, then right-shift the result (>> operator) to align them with bit zero.
  • user3267877
    user3267877 about 10 years
    Hi webuster, I found yours a little easy to understand so i tried calculating the value but it seems to output wrong result. I used programmers calculator in hex mode. 1<<16 = 400000-1= 3fffff. Now 3fffff << 10 = 3fffff0000 & D7448eab = D7440000 = 11010111010001000000000000000000 (binary). The output is not what is required. Let me know if im doing something wrong
  • webuster
    webuster about 10 years
    (1 << 16) - 1 = ffff. What you're doing in Calculator is you're Lsh-ing 1 with 16 in HEX, which is 22 in decimal. Try Lsh-ing 1 with 10 in hex (16 in binary) and you'll get the correct result.
  • user3267877
    user3267877 about 10 years
    I did that too and the result is the same (1<<10)-1=ffff <<10 = ffff0000 & 0xD7448EAB = D7440000 = 11010111010001000000000000000000. The first six bits are to be ignored and if you check they are not being ignored and hence the incorrect result. The result i need is 1101 0001 0010 0011
  • user3267877
    user3267877 about 10 years
    unsigned int number = 0xD7448EAB; unsigned int value = (number & 0x3FFFC00) >> 10; this is absolutely correct. the only change needed is >>8 instead of >>10. I got this working. Thank you
  • Remy Lebeau
    Remy Lebeau about 10 years
    Using >> 10 is correct for what you asked for. Using >> 8 would give you bits 8 through 23 instead, which is not what you asked for.
  • Remy Lebeau
    Remy Lebeau about 10 years
    Why use = and &= separately, instead of using = and & in the same statement?
  • webuster
    webuster about 10 years
    You were making the same mistake, Lsh-ing and Rsh-ing with base 16 not in decimal. Lsh-ing with 10 in HEX means shifting by 16. The Hex digit for the decimal 10 is A. So the way you should shift is FFFF << A. So the steps are (with respect to Calculator): 1 << 10, then -1 => FFFF. Then FFFF << A = 3FFFC00. Then D7448EAB & 3FFFC00 is 3448C00 which in binary is 1101 0001 0010 0011 as you wanted.
  • user3267877
    user3267877 about 10 years
    Maybe a dumb question but im honestly a noob when it comes to C.I can use this expression - ((1 << 10) -1) << A in C code, correct? There is no difference between C and the one we use for calculator?
  • webuster
    webuster about 10 years
    Nono. In C the "default" numeric base is decimal, not hex. So you would do ((1 << 16) - 1) << 10) as in my reply, that would be your mask. Then AND your number with the mask and you'll get the correct result. IF you want to use hex like you did with the calculator, put 0x in front of the numbers you use, as in ((1 << 0x10) - 1) << 0xA). The mask is equivalent.
  • user3267877
    user3267877 about 10 years
    Webbuster this was pretty helpful. Thank you very much
  • samiam
    samiam about 10 years
    Simpler to follow for a beginner in C (it let me comment each change) and & has precedent issues (which can really throw off someone who doesn't know the ropes of C)
  • Catherine
    Catherine over 7 years
    How would you adapt this solution to extract an unknown range of bits? Like a function extract(unsigned num, unsigned hi, unsigned lo) that returns the bits in range hi to lo of num?