Home > Mobile >  Extract the first n bits of an unsigned int in C
Extract the first n bits of an unsigned int in C

Time:07-18

I want to write a function which extracts the first n bits starting by the MSB of a given uint16. I'm working with bitmasks for the first time and haven't found an elegant solution as far. I've written a similar function for the last n bits starting by the LSB which uses a bitmask looking like

(1 << n) - 1

and think this works.

If I have the value 0b1010001100000101 my function creates the bitmask (for n = 3 it looks like 0b111) and after that, uses the &-operator to check if the bits are present.

Which way should I use to get the first n bits?

I would be really happy if someone explain this to me because I want to understand how it works.

Thank you!

CodePudding user response:

You do not need a mask to extract the most significant bits of an unsigned int: just shift the value right by the width of the type less the number of bits:

#include <stdint.h>

uint16_t extract_msbits(uint16_t x, int n) {
    if (n <= 0)
        return 0;
    else
    if (n < 16)
        return x >> (16 - n);
    else
        return x;
}

If the extracted bits should stay in place, you really mean to mask off the lower bits. Here is a solution:

#include <limits.h>

uint16_t mask_msbits(uint16_t x, int n) {
    if (n <= 0)
        return 0;
    else
    if (n < 16)
        return x & (~0U << (16 - n));
    else
        return x;
}

The expression (1 << n) - 1 has undefined behavior if n < 0 or n is greater or equal to 15 if the width of int is 16. For this reason, you should write (1U << n) - 1 to compute a mask for an unsigned int or a uint16_t, but it still fails on 16-bit systems if n is 16 or larger, which might be a valid argument for the function. To avoid this issue, either use a test to compute a mask of 0xFFFF for n == 16 or use (1UL << n) - 1.

CodePudding user response:

A simple solution:

What you can do is to right shift your uint16_t integral value, by 16 - n bits. Thus, only n bits remain.

Assume you have a type uint16_t and an integral object of this type with value 43558 named x:

uint16_t x = 43558U;

Right shifting by 16 - n bits:

x >>= (16U - n);

// Or if you want to keep x intact

uint16_t y = x >> (16U - n);

Will store the first n bits in x. However, it will be padded with zeroes. Therefore, you need to perform bitwise AND (&) to test for the presence of 1 and 0 in this resulting x.

In case you are learning:

  • Read these answers on StackOveflow about grabbing n bits
  • Read this amazing article on bit manipulation

CodePudding user response:

If all you want is to mask off all but the top n bits, you can simply shift right then left:

#include <assert.h>
#include <stdint.h>

/**
 * @brief Masks all but top `n` bits
 * @param number Input number
 * @param n Number of bits to preserve. Must be >= 0 and <= 16
 * @return Original number with all but the top `n` bits masked off
 */
uint16_t msb_n(uint16_t number, const unsigned n) {
    assert(n <= 16 && "cannot shift more than 16 bits.");
    number >>= (16 - n);
    number <<= (16 - n);
    return number;
}
  • Related