2
votes

So I'm having trouble trying to isolate a certain amount of bits through masking. Essentially I have a certain amount of bits I want to mask (let's call it offset) from a single byte (so 8 bits). There are 2 kinds of masks I need help with:

First one: say the offset = 4 and I have a byte with binary 1110 0001. I want to mask the last bits of size offset such that I can have a final byte of 1110 0000 (so mask the last 4 bits).

Second one: say the offset is 3 and I have a byte with binary 1011 0010. I want to now mask the first few bits of size offset so that I have a final byte of 0001 0010.

I've pasted the code I have so far. Currently it does not work for the first mask I want to create as it masks the first few bits not the last. I'm not sure if I'm creating the mask correctly.

uint8_t mask = (1 << offset) - 1;
byte = (byte & mask);
1
You need to position the mask's bits uint8_t mask = ((1 << offset) - 1) << pos; and use the complement of mask (~mask) to clear the masked bits from the byte byte = (byte & ~mask); . - isrnick
Welcome to StackOverflow! Please take the tour and read "How to Ask". Then come back and edit your question, please. -- Would you mind to provide a minimal reproducible example? Or at least some specific examples of offset and and mask you expect from it? Like: offset = 3 gives mask = 00011110 (setting the left 3 bits to 0, keep the next 4 bits, set the rightmost bit to 0). - the busybee

1 Answers

2
votes

To mask off the low offset bits, your computation for mask is OK, but the expression is not. It should be:

byte = (byte & ~mask);

or simply:

byte &= ~mask;

To compute a mask from an offset with (1 << offset) - 1, you should beware that offset must be less than the number of bits in the type of 1. 1 being an int, if means offset < 32, so you cannot compute the mask for a 32-bit field this way.

Furthermore, even 31 poses a problem because (1 << 31) exceeds the range of type int. To avoid this, you should write (1U << offset) - 1 and you must first test if offset < 32.

A better alternative that works for offset values from 1 to 32 is this:

unsigned mask = ~0U >> (sizeof(unsigned) * CHAR_BIT - offset);
bits &= ~mask;

or simpler with an opposite mask:

bits &= ~0U << offset;

Here are macros to get, clear and set a bitfield in an unsigned int:

#define GET_MASK(width)               (~0U >> (sizeof(unsigned) * CHAR_BIT - (width)))
#define GET_FIELD(x, pos, width)      (((x) >> (pos)) & GET_MASK(x, width))
#define CLEAR_FIELD(x, pos, width)    (((x) &= ~(GET_MASK(x, width) << (pos)))
#define SET_FIELD(x, pos, width, val) ((x) = ((x) & ~(GET_MASK(x, width) << (pos))) | ((val) << (pos)))