In C, the 31 most significant bits of a uint64_t value are simply b shifted right (64-31)=33 bits:

Code:

uint64_t result = (uint64_t)value >> 33U;

In general, the right shift count is (number of bits - number of highest bits needed).

If you want to (further) limit the result to fewer bits, mask with ((((type)1) << bits)-1). This works, because mathematically that is 2bits-1, which in binary has bits least significant bits set.

In C, you need to cast to type (uint64_t, above), so that the shift is done with the desired result type. Logically, you need value to first be converted to the result type, and then operated on, not the other way around.

For example, this copies length bits starting from offset (least significant bit numbered 0), into the least significant bits in the result:

The if clause is needed, because (uint64_t)1 << 64) overflows. (It is not used always in practice, because practically all architectures use two's complement rules, and wraparound such that ((uint64_t)1 << 64)-1 == (uint64_t)-1 == ~(uint64_t)0 = all bits set.

With the if clause, the above works on all architectures, and is not dependent on byte order (little or big endian) or even on internal representation of integers. You only need to remember that regardless of byte order, the above uses offset 0 for the least significant bit, increasing towards more important bits (63 for the most significant bit in an uint64_t), and returns the result in least significant bits.

I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.