20

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);. Function should handle each n from 0 to 32.

Especially value n==32 should be handled.

Cartesius00
  • 23,584
  • 43
  • 124
  • 195
  • 1
    by last do you mean high order or low order? – Jim Rhodes Nov 14 '11 at 21:52
  • this question handles any values in the range \[0, 64\]: [Creating a mask with N least significant bits set](https://stackoverflow.com/q/52573447/995714) – phuclv Jul 19 '19 at 05:00

8 Answers8

34

Here's a method that doesn't require any arithmetic:

~(~0u << n)
Eric
  • 95,302
  • 53
  • 242
  • 374
  • 1
    +1, no memory access, no branches and no special cases. I consider this to be the most elegant solution. – DarkDust Feb 02 '13 at 13:29
  • 12
    @DarkDust: Except this (sometimes) fails for n = 32, since shifting a `uint_32t` by `32` is undefined behaviour. – Eric Feb 02 '13 at 15:52
24

If you meant the least-significant n bits:

((uint32_t)1 << n) - 1

On most architectures, this won't work if n is 32, so you may have to make a special case for that:

n == 32 ? 0xffffffff : (1 << n) - 1

On a 64-bit architecture, a (probably) faster solution is to cast up then down:

(uint32_t)(((uint64_t)1 << n) - 1)

In fact, this might even be faster on a 32-bit architecture since it avoids branching.

Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
  • @Marcelo: I guess it works even without u64 test, just the first version, doesn't it? – Cartesius00 Nov 14 '11 at 22:02
  • 2
    @TonyK: No, it doesn't work, because shifting a 32-bit integer by 32-bits isn't supported on most architectures (certainly not on Intel). – Marcelo Cantos Nov 14 '11 at 22:03
  • 2
    Right: "The behavior is undefined if the right operand is...equal to the length in bits of the promoted left operand." [C++11 §5.8/1] – James McNellis Nov 14 '11 at 22:03
  • @TonyK n == 32 shifts one past the width, which is undefined behavior. – nos Nov 14 '11 at 22:04
  • @Marcelo: Anyway, Marcelo, answer accepted. Good to know about 1<<32 issue. – Cartesius00 Nov 14 '11 at 22:06
  • I don't think that this answer is correct. `1` is of type `int` and `1 << n` could go wrong for much more cases than you assume. A first shot would be to use `1U << n` but then the OP explicitly wanted `uint32_t`. For that the correct thing would be to use `UINT32_C(1) << n`. – Jens Gustedt Nov 14 '11 at 22:27
  • @MichaelKrelin-hacker: Nice. Though I assume you meant `(uint32_t)((-1)>>(32-n))` – Marcelo Cantos Nov 14 '11 at 22:28
  • oops. But no, I meant `(uint32_t)`, with signed it will stay with all bits set for all values of n. – Michael Krelin - hacker Nov 14 '11 at 22:29
  • I see you edited your guess, still no, I want to cast before shifting. – Michael Krelin - hacker Nov 14 '11 at 22:31
  • @JensGustedt: Other than 32, which has already been discussed, which other value(s) doesn't my answer handle correctly? – Marcelo Cantos Nov 14 '11 at 22:41
  • @MichaelKrelin-hacker: Ah, sorry; I misunderstood what you were trying to do. Yes, that works, but it also has an edge-case: n == 0. – Marcelo Cantos Nov 14 '11 at 22:44
  • 2
    @MarceloCantos, IRC left shift with `31` on an 32 bit integer leads to overflow => undefined behavior. And then `int` is only guaranteed to be 16 bit wide by the standard. In summary your version can fail for values from `16` to `31`. – Jens Gustedt Nov 14 '11 at 22:54
  • 2
    @MarceloCantos, also in your faster version, what is the purpose of cast `n` to `uint64_t`? You should "cast" the `1` this is what determines the type of the expression. – Jens Gustedt Nov 14 '11 at 22:58
  • Branching would most probably not be an issue here... there are specific assembly instructions to handle the simpler `cond?a:b` cases like this. Try to compile into assembly and check the output. – David Rodríguez - dribeas Nov 15 '11 at 01:19
  • @JensGustedt: Ah yes; good points; I've fixed it. When you said, "much more cases", I thought you meant something more calamitous. – Marcelo Cantos Nov 15 '11 at 04:00
  • @MarceloCantos, at least the 31 is calamitous. Please fix it by just putting a `1U` instead of `1` as a base of the shift. – Jens Gustedt Nov 15 '11 at 07:24
  • You can just use `~0UUL` or `~0UL` instead of `0xsomanyf`. It'll be more platform independent as you don't need to know the exact size of `int` or `long` or whatever. – xuhdev Nov 16 '14 at 06:46
  • @xuhdev: The OP stipulated uint32_t. – Marcelo Cantos Nov 17 '14 at 10:46
  • If you can't shift all the way through, then shift half of that and then do the rest: (1 << n/2 << n - n/2) - 1 (sort of a joke) – wonder.mice Oct 06 '15 at 08:42
  • there are many ways to handle that case without a branch: [Creating a mask with N least significant bits set](https://stackoverflow.com/q/52573447/995714) – phuclv Jul 19 '19 at 04:58
10

The other answers don't handle the special case of n == 32 (shifting by greater than or equal to the type's width is UB), so here's a better answer:

(uint32_t)(((uint64_t)1 << n) - 1)

Alternatively:

(n == 32) ? 0xFFFFFFFF : (((uint32_t)1 << n) - 1)
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
5
const uint32_t masks[33] = {0x0, 0x1, 0x3, 0x7 ...

void setbits(uint32_t *x, int n)
{
   *x |= masks[n];
}
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
TJD
  • 11,800
  • 1
  • 26
  • 34
1

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
Harvey
  • 5,703
  • 1
  • 32
  • 41
  • Your case for `n >= 32` does not help, since the prior unchecked shift will still be done, and if `n >= sizeof(TheType) * CHAR_BIT`, that's UB, which isn't affected by whether you try to paper over it after the fact. – underscore_d Oct 31 '17 at 00:06
  • 1
    Perhaps "goals" should also include "readable/understandable code" :-) – paxdiablo Oct 31 '17 at 01:12
  • @paxdiablo Yeah, that's why I stated the goals: to indicate that readable code was NOT the goal. :) – Harvey Oct 31 '17 at 13:11
  • @underscore_d What's "UB" mean? It's ok if the first line is produces a bad value for `n>=32` because the next line corrects it. I've added a program to the answer that proves the algorithm. Honestly, it has been so long that I needed the program to prove it to myself. – Harvey Oct 31 '17 at 13:36
  • 1
    @Harvey Undefined behaviour poisons the entirety of a program as soon as you invoke it. You cannot "correct it" by overwriting it afterwards. And it means absolutely nothing if you find that the code *happens* to produce the correct results when tested, because the UB means that it's not **required** to and can break at any time (when ported to a different architecture, compiler, Standard revision, etc.). Code that invokes UB is bad and shouldn't be posted as a recommendation, and that's the end of it. – underscore_d Oct 31 '17 at 13:38
  • @underscore_d I get your point, and I respectfully disagree. Example: `x = uint32_t(1) << 40; x = 7;` Even if the first statement is undefined behavior (for some values, e.g. 40), the second line is very much defined behavior and the result is ok. Unless "undefined behavior" includes crashing an app or memory corruption, it's good. – Harvey Oct 31 '17 at 14:04
  • @underscore_d To be fair, I was surprised that `<<` doesn't mean shift for my current compiler. It clearly means shift with rotation which I wouldn't have expected (undefined behavior). – Harvey Oct 31 '17 at 14:06
  • 2
    @Harvey There's nothing to disagree about. The letter of the Standard is that once UB occurs, the behaviour of the entire program is undefined. It doesn't matter if you then do something that completely replaces the object; UB has occurred, and it can't be undone. Sure, practically, probably nothing will go wrong, as compilers don't make it their mission to ruin your life. But it's formally ill-formed code, so it's irresponsible to portray it as being fine. – underscore_d Oct 31 '17 at 14:07
  • @underscore_d I agree with you in principle. In this case, I'm ok with this specific code because I think "undefined behavior" probably means "undefined, but sane". In other words, no compiler writer is going to guarantee that the value returned is what you expected, but they will probably guarantee not to modify anything other than that value producing no side effects. I'll add a comment with your warning. – Harvey Oct 31 '17 at 14:14
1

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];
    }
}
Jim Rhodes
  • 5,021
  • 4
  • 25
  • 38
0
((((1 << (n - 1)) - 1) << 1) | 1)

Set last n bits. n must be > 0. Work with n = 32.

Enyby
  • 4,162
  • 2
  • 33
  • 42
0

If you mean the most significant n bits:

-1 ^ ((1 << (32 - n)) - 1)