7

Let's take int as an example:

int SetBitWithinRange(const unsigned from, const unsigned to)
{
    //To be implemented 
} 

SetBitWithinRange is supposed to return an intin which all and only the bits starting at bit from to bit to are set, when from is smaller than to and both are in the range of 0 to 32.

e.g.: int i = SetBitWithinRange(2,4) will result in i having the value of 0b00...01100

Subway
  • 5,286
  • 11
  • 48
  • 59
  • Check this [post](http://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit-in-c-c) and adapt it in a loop – n0p Mar 26 '14 at 13:43
  • 2
    Why don't you try implementing it and see what you can come up with. – John Zwinck Mar 26 '14 at 13:43
  • 1
    This seems like it could be homework.. anyway, here's an idea. Set `n` bits, then shift them by `from`. – harold Mar 26 '14 at 13:44
  • `int value = 0; int bit = (1 << from); value |= bit;` -- apply that to the remaining bits. – mah Mar 26 '14 at 13:44
  • @JohnZwinck, Surely I can do it in a loop. I was wondering though if there is a faster way of doing that. – Subway Mar 26 '14 at 13:45
  • @Subway there is, in fact there are several ways, and they differ in what happens in edge cases (for example, if the range is empty, if the range is all bits, or if `to` includes the top bit) – harold Mar 26 '14 at 13:45
  • 1
    Maybe you should have put the requirement about NOT using a loop in the question. – Aleksei Petrenko Mar 26 '14 at 13:49
  • In your example, it looks like only bits 2 and 3 are set (assume counting from 0, starting from the right). Does that mean Param#1 is *inclusive*, but Param#2 is *exclusive*? That seems confusing. For `SetBit(2,4)`, I would expect the output to be `0b00011100` (bits 2 through 4 set... again assuming 0-based, from right). – abelenky Mar 26 '14 at 14:16
  • 1
    Why the range 0 to 32? It seems artificial. I'd expect range 0 to 31 (or better `0 to sizeof(unsigned)*CHAR_BIT-1`) or 0 to `UINT_MAX`? – chux - Reinstate Monica Mar 26 '14 at 14:36
  • @chux has an excellent point. Range 0-32 actually represents **33** places. You should choose (and state clearly) if this is 0-based (0-31 range), or 1-based (1-32 range). – abelenky Mar 26 '14 at 14:37
  • "when `from` is smaller than `to` and both are in the range of 0 to 32." is contradictory. Both `to` and `from` can't be in the same range _and_ `from` is smaller than `to`. – chux - Reinstate Monica Mar 26 '14 at 14:46

7 Answers7

6

Here are some ways. First, some variants of "set n bits, then shift by from". I'll answer in C# though, I'm more familiar with it than I am with C. Should be easy to convert.

uint nbits = 0xFFFFFFFFu >> -(to - from);
return nbits << from;

Downside: can't handle an empty range, ie the case where to <= from.

uint nbits = ~(0xFFFFFFFFu << (to - from));
return nbits << from;

Upside: can handle the case where to = from in which case it will set no bits.
Downside: can't handle the full range, ie setting all bits.

It should be obvious how these work.

Alternatively, you can use the "subtract two powers of two" trick,

(1u << to) - (1u << from)

Downside: to can not be 32, so you can never set the top bit.

Works like this:

01000000
  ^^^^^^ "to" zeroes
     100
      ^^ "from zeroes"
-------- -
00111100

To the right of the 1 in the "from" part, it's just zeroes being subtracted from zeroes. Then at the 1 in the "from" part, you will either subtract from a 1 (if to == from) and get 0 as a result, or you'll subtract a 1 from a 0 and borrow all the way to the 1 in the to part, which will be reset.

All true bitwise methods that have been proposed at the time of writing have one of those downsides, which raises the question: can it be done without downsides?

The answer is, unfortunately, disappointing. It can be done without downsides, but only by

  1. cheating (ie using non-bitwise elements), or
  2. more operations than would be nice, or
  3. non-standard operations

To give an example of 1, you can just pick any of the previous methods and add a special case (with an if or ternary operator) to work around their downside.

To give an example of 2: (not tested)

uint uppermask = (((uint)to >> 5) ^ 1) << to;
return uppermask - (1u << from);

The uppermask either takes a 1 and shifts it left by to (as usual), or it takes a 0 and shifts it left (by an amount that doesn't matter, since it's 0 that's being shifted), if to == 32. But it's kind of weird and uses more operations.

To give an example of 3, shifts that give zero when you shift by the operand size or more would solve this very easily. Unfortunately, that kind of shift isn't too common.

harold
  • 61,398
  • 6
  • 86
  • 164
5

A common way to do this somewhat efficiently would be this:

uint32_t set_bits_32 (uint32_t data, uint8_t offset, uint8_t n)
{
  uint32_t mask = 0xFFFFFFFF >> (32-n);

  return data | (mask << offset);
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
4

I'd go with something like that:

int answer = 0;
unsigned i = from;
for (; i <= to; ++i)
  answer |= (1 << i);

return answer;

Easy to implement & readable.

I think that the fastest way would be to pre-calculate all possible values (from (0, 0) to (32, 32), if you know that you'll use this only for 32-bit integers). In fact there are about 1000 of them.

Then you'll end up with O(1) solution:

answer = precalcTable[from][to];
Aleksei Petrenko
  • 6,698
  • 10
  • 53
  • 87
4

OK, I'm taking up the gauntlet that @JohnZwinck has thrown towards me.

How about: return (to<32 ? (1<<to) : 0) - (1<<from);

Of course this is without fully checking for validity of from and to.

Edited according to @JosephQuinsey comments.

Subway
  • 5,286
  • 11
  • 48
  • 59
  • 2
    Perhaps change `1` to `1U`, as `1 << 31` is Undefined Behavior on a 32-bit machine. – Joseph Quinsey Mar 26 '14 at 13:57
  • 1
    And your question said "range of `0` to `32`". But `1U << 32` is IIRC undefined behavior on a typical 32-bit machine. – Joseph Quinsey Mar 26 '14 at 14:00
  • @JosephQuinsey things aren't undefined on machines, they're undefined in the standard. On a typical machine it's defined, but often defined in such a way that it won't work (`1 << 32 == 1`, often). – harold Mar 26 '14 at 14:03
  • Just because some code is compact doesn't necessarily mean it is efficient. You have one comparison, two shifts, one subtraction and a branch. If you look at the code I posted, it is very similar but with a bitwise OR instead of branching. Branching may or may not make the whole code less efficient, depending on CPU. – Lundin Mar 26 '14 at 14:09
  • This seems like a cheat. You're asking for bit-manipulation, but you're using a branch. – harold Mar 26 '14 at 14:14
  • 1
    @harold: For `unsigned x = 1U; printf("%u %u\n", 1U << 32, x << 32);` gcc with `-O0` gives the _inconsistent_ results `0 1` (and this is what I would expect it to do). (It also warns "left shift count >= width of type", twice.) – Joseph Quinsey Mar 26 '14 at 14:21
  • @JosephQuinsey of course it does, but it's GCC doing that, not the machine. On any sane machine (and even x86, often accused of not being sane), the machine instructions for shifts are perfectly well-defined for any input. – harold Mar 26 '14 at 14:25
  • @JosephQuinsey I'm really not arguing that it's defined in C. Almost nothing is. But you said "undefined behaviour on a typical 32-bit *machine*", which is completely untrue. – harold Mar 26 '14 at 14:50
  • Regarding `<< 32`, the C11 standard 6.5.7[3] states: _"If the value of the right operand is ... equal to the width of the promoted left operand, the behavior is undefined."_ This is what I meant by my sloppy phrase _typical 32-bit machine_. Of course on many machines, an int can have various widths, depending on the compiler settings. – Joseph Quinsey Mar 26 '14 at 15:17
  • @JosephQuinsey oh right, so you mean in C, and just put the "32-bit machine" in there to mean "unsigned ints are 32 bits" – harold Mar 26 '14 at 15:23
1

maybe: (( 1 << to ) - (1 << from)) | (1 << to)

This will also set the to and from bits as requested

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
1

Here's my answer. (updated)

unsigned int SetBits(int from, int to)
{    
    return (UINT_MAX >> (CHAR_BIT*sizeof(int)-to)) & (UINT_MAX << (from-1));
}

SetBits(9,16); ==> 0b 1111 1111 0000 0000

SetBits(1,1); ==> 0b 0000 0001  // Just Bit #1
SetBits(5,5); ==> 0b 0001 0000  // Just Bit #5
SetBits(1,4); ==> 0b 0000 1111  // Bits #1, #2, #3, and #4 (low 4 bits) 
SetBits(1,32); ==> 0b 1111 1111 1111 1111  // All Bits

However, SetBits(0,0); does NOT work for turning all bits off.

My assumptions:

  • Bits are 1-based, starting from the right.
  • Bytes are 8-bits.
  • Ints can be any size (16, 32 or 64 bit). sizeof(int) is used.
  • No checking is done on from or to; caller must pass proper values.
abelenky
  • 63,815
  • 23
  • 109
  • 159
0

Can be done in this way as well, pow can be implemented using shift operations.

{
    unsigned int i =0;
    i = pow(2, (to-from))-1;
    i = i <<from;
    return i;
}
user207064
  • 665
  • 5
  • 14
  • The pow function as defined by the C standard is using floating point, so it is utterly ineffective. I take it you are suggesting to make your own, integer-based pow() function? Still, I don't see how that would improve efficiency. – Lundin Mar 27 '14 at 08:55