Creating a mask with N least significant bits set

前端 未结 6 1705
离开以前
离开以前 2021-01-18 01:00

I would like to create a macro or function1 mask(n) which given a number n returns an unsigned integer with its n least sig

6条回答
  •  梦毁少年i
    2021-01-18 01:14

    Try

    unsigned long long mask(const unsigned n)
    {
      assert(n <= 64);
      return (n == 64) ? 0xFFFFFFFFFFFFFFFFULL :
         (1ULL << n) - 1ULL;
    }
    

    There are several great, clever answers that avoid conditionals, but a modern compiler can generate code for this that doesn’t branch.

    Your compiler can probably figure out to inline this, but you might be able to give it a hint with inline or, in C++, constexpr.

    The unsigned long long int type is guaranteed to be at least 64 bits wide and present on every implementation, which uint64_t is not.

    If you need a macro (because you need something that works as a compile-time constant), that might be:

    #define mask(n) ((64U == (n)) ? 0xFFFFFFFFFFFFFFFFULL : (1ULL << (unsigned)(n)) - 1ULL)
    

    As several people correctly reminded me in the comments, 1ULL << 64U is potential undefined behavior! So, insert a check for that special case.

    You could replace 64U with CHAR_BITS*sizeof(unsigned long long) if it is important to you to support the full range of that type on an implementation where it is wider than 64 bits.

    You could similarly generate this from an unsigned right shift, but you would still need to check n == 64 as a special case, since right-shifting by the width of the type is undefined behavior.

    ETA:

    The relevant portion of the (N1570 Draft) standard says, of both left and right bit shifts:

    If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

    This tripped me up. Thanks again to everyone in the comments who reviewed my code and pointed the bug out to me.

提交回复
热议问题