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
nbits - 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;
}
