Set last `n` bits in unsigned int

后端 未结 9 1290
小蘑菇
小蘑菇 2020-12-17 15:30

How to set (in most elegant way) exactly n least significant bits of uint32_t? That is to write a function void setbits(uint32_t *x, int n);<

相关标签:
9条回答
  • 2020-12-17 16:07

    If n is zero then no bits should be set based on the question.

    const uint32_t masks[32] = {0x1, 0x3, 0x7, ..., 0xFFFFFFFF};
    
    void setbits(uint32_t *x, int n)
    {
        if ( (n > 0) && (n <= 32) )
        {
            *x |= masks[--n];
        }
    }
    
    0 讨论(0)
  • 2020-12-17 16:08

    The function with a simple test:

    #include <stdio.h>
    #include <stdint.h>
    
    void setbits(uint32_t *x, int n)
    {
      *x |= 0xFFFFFFFF >> (32 - n);
    }
    
    int main()
    {
      for (int n = 1; n <= 32; ++n)
      {
        uint32_t x = 0;
        setbits(&x, n);
        printf("%2d: 0x%08X\n", n, x);
      }
      getchar();
      return 0;
    }
    
    0 讨论(0)
  • 2020-12-17 16:13

    Goals:

    • no branches (including parameter check of n)
    • no 64-bit conversions
    void setbits(uint32_t *x, unsigned n) {
        // As @underscore_d notes in the comments, this line is
        // produces Undefined Behavior for values of n greater than
        // 31(?). I'm ok with that, but if you're code needs to be
        // 100% defined or you're using some niche, little-used
        // compiler (perhaps for a microprocesser?), you should
        // use `if` statements. In fact, this code was just an
        // an experiment to see if we could do this in only 32-bits
        // and without any `if`s. 
        *x |= (uint32_t(1) << n) - 1;
        // For any n >= 32, set all bits. n must be unsigned
        *x |= -uint32_t(n>=32);
    }
    

    Note: if you need n to be of type int, add this to the end:

        // For any n<=0, clear all bits
        *x &= -uint32_t(n>0);
    

    Explanation:

        *x |= -uint32_t(n>=32);
    

    When n>=32 is true, x will be bitwise-ORed with 0xFFFFFFFF, yielding an x with all bits set.

        *x &= -uint32_t(n>0);
    

    This line states that as long as any bit should be set, n>0, bitwise-AND x with 0xFFFFFFFF which will result in no change to x. If n<=0, x will be bitwise-ANDed with 0 and consequently result in a value of 0.

    Sample program to show the algorithm works:

    #include <stdio.h>
    #include <stdint.h>
    
    void print_hex(int32_t n) {
      uint32_t x = (uint32_t(1) << n);
      printf("%3d:  %08x  |%08x  |%08x  &%08x\n",
             n, x, x - 1,
             -uint32_t(n>=32),
             -uint32_t(n>0));
    }
    
    void print_header() {
      //        1:  00000002  |00000001  |00000000  &ffffffff
      printf("  n:   1 << n    (1<<n)-1   n >= 32     n <= 0\n");
    }
    
    void print_line() {
      printf("---------------------------------------------\n");
    }
    
    int main() {
      print_header();
      print_line();
      for (int i=-2; i<35; i++) {
        print_hex(i);
        if (i == 0 || i == 31) {
          print_line();
        }
      }
      return 0;
    }
    

    Output (broken up and annotated):

    For n < = 0, the last step ANDs with 0 ensuring the result is 0.

      n:   1 << n    (1<<n)-1   n >= 32     n <= 0
    ---------------------------------------------
     -2:  40000000  |3fffffff  |00000000  &00000000
     -1:  80000000  |7fffffff  |00000000  &00000000
      0:  00000001  |00000000  |00000000  &00000000
    

    For 1 <= n <= 31, the last two steps "OR 0, AND 0xffffffff" result in no change to the number. The only step that matters is the "OR (1<

      n:   1 << n    (1<<n)-1   n >= 32     n <= 0
    ---------------------------------------------
      1:  00000002  |00000001  |00000000  &ffffffff
      2:  00000004  |00000003  |00000000  &ffffffff
      3:  00000008  |00000007  |00000000  &ffffffff
      4:  00000010  |0000000f  |00000000  &ffffffff
      5:  00000020  |0000001f  |00000000  &ffffffff
      6:  00000040  |0000003f  |00000000  &ffffffff
      7:  00000080  |0000007f  |00000000  &ffffffff
      8:  00000100  |000000ff  |00000000  &ffffffff
      9:  00000200  |000001ff  |00000000  &ffffffff
     10:  00000400  |000003ff  |00000000  &ffffffff
     11:  00000800  |000007ff  |00000000  &ffffffff
     12:  00001000  |00000fff  |00000000  &ffffffff
     13:  00002000  |00001fff  |00000000  &ffffffff
     14:  00004000  |00003fff  |00000000  &ffffffff
     15:  00008000  |00007fff  |00000000  &ffffffff
     16:  00010000  |0000ffff  |00000000  &ffffffff
     17:  00020000  |0001ffff  |00000000  &ffffffff
     18:  00040000  |0003ffff  |00000000  &ffffffff
     19:  00080000  |0007ffff  |00000000  &ffffffff
     20:  00100000  |000fffff  |00000000  &ffffffff
     21:  00200000  |001fffff  |00000000  &ffffffff
     22:  00400000  |003fffff  |00000000  &ffffffff
     23:  00800000  |007fffff  |00000000  &ffffffff
     24:  01000000  |00ffffff  |00000000  &ffffffff
     25:  02000000  |01ffffff  |00000000  &ffffffff
     26:  04000000  |03ffffff  |00000000  &ffffffff
     27:  08000000  |07ffffff  |00000000  &ffffffff
     28:  10000000  |0fffffff  |00000000  &ffffffff
     29:  20000000  |1fffffff  |00000000  &ffffffff
     30:  40000000  |3fffffff  |00000000  &ffffffff
     31:  80000000  |7fffffff  |00000000  &ffffffff
    

    For n >= 32, all bits should be set and the "OR ffffffff" step accomplishes that regardless of what the previous step may have done. The n <= 0 step is then a noop as well with AND ffffffff.

      n:   1 << n    (1<<n)-1   n >= 32     n <= 0
    ---------------------------------------------
     32:  00000001  |00000000  |ffffffff  &ffffffff
     33:  00000002  |00000001  |ffffffff  &ffffffff
     34:  00000004  |00000003  |ffffffff  &ffffffff
    
    0 讨论(0)
提交回复
热议问题