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