Re: portable way to get highest bit set?

Kaz Kylheku <[email protected]> Wed, 11 Oct 2023 21:57:06 -0000 (UTC)
Newsgroups comp.lang.c,alt.comp.lang.c
Organization A noiseless patient Spider
Message-ID <[email protected]>
On 2023-10-11, candycanearter07 <[email protected]> wrote:
> Hi,
>
> What is the best/most portable way to get the highest bit set?
>
> ie. 011010001
> to  010000000

I just noticed you really want the *mask* of the highest bit set,
not its position.

That's pretty easy to do in a portable way.

First we calculate this fully saturated mask:

      011111111

then, we just right shift that to produce this mask:


      001111111

and XOR them together to get

      010000000

Now how do we get that first mask? Like ethis:

Start with x:

      011010001     x

OR it with x >> 1:

   |  001101000     x >> 1
   ------------
   =  011111001

Take that to be the new x. OR it with x >> 2

      011111001
   |  000111110
   ------------
   =  011111111

And here we are done; but we continue to x >> 4 and x >> 8.

E.g. 32 bit code:

   uint32_t fill_mask_down(uint32_t x)
   {
     x |= x >> 1;    // e.g.   1000...0000 -> 1100...0000
     x |= x >> 2;    // e.g.   1100...0000 -> 1111...0000
     x |= x >> 4;    // e.g.   11110000...  -> 11111111...
     x |= x >> 8;
     x |= x >> 16;

     return x;
   }

Thus:

  uint32_t isolate_highest_bit(uint32_t x)
  {
     uint32_t m = fill_mask_down(x);
     return m ^ (m >> 1);
  }

Note that this has no branches whatsoever. There are data hazards
because we are updating an accumulator in place; that's likely
going to cost some pipeline-level parallelism.

Note that the numbeer of |= steps in fill_mask_down is the log2
of the number of bits. The 64 bit code just ads x |= x >> 32.

-- 
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @[email protected]
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.