2

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!

1
  • 1
    If by 'first' you mean the least significant bits they are (uval << (16-n)) >> (16-n). If you mean the most significant bits they are (uval >> (16-n)) << (16-n). In each case, the unwanted bits are shoved off the end, and zeros are shifted back in. Commented Jul 17, 2022 at 17:56

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

4 Comments

"... but it still fails on 16-bit systems" How to solve that?
@Zakk: I amended the answer with 2 solutions, none of which is fully satisfying.
Your else-if formatting is rather curious. Is this formatting used by any projects?
@AyxanHaqverdili: I use this style in quickjs and qemacs. It is consistent with indenting the body of else if by the same amount as the initial if branch, whether else and if appear on the same line or not. Breaking the line between else and if allows for all the conditions to align vertically.
0

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

9 Comments

I understand. Your answer provides the correct solution, but ASAIK, OP is not going to push this code to production, so I am just providing many simple alternative and learning resources
The C++ references are not precise enough regarding the bit correspondance: MSdoc: The N bits in a bitset are indexed by integer values from 0 to N - 1, where 0 indexes the first bit position and N - 1 the final bit position. and operator[] returns a reference to a bit at a specified position in a bitset if the bitset is modifiable; otherwise, it returns the value of the bit at that position.
Also, For unsigned a and for signed and non-negative a, the value of a >> b is the integer part of a/2^b. This is from C++17 standard. For a = 0, this is defined behavior
@chqrlie You are correct, I just tested bitset[0] and it indeed is LSB. Sorry for this mistake! I removed my C++ reference. Thanks once again for pointing out.
I am amazed at how imprecise both cppreference and the MS doc is!
|
0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.