4

I have been trying a lot to find an exact replacement for the Java's Integer.highestOneBit(int) in C#.

I even tried finding its source code but to no avail.

JavaDocs tells that this function:

Returns an int value with at most a single one-bit, in the position of the highest-order ("leftmost") one-bit in the specified int value.

So how would I go about implementing this in C#? Any help or link/redirection is appreciated.

phuclv
  • 37,963
  • 15
  • 156
  • 475
Nathan
  • 1,303
  • 12
  • 26
  • You can always find the source code for any standard Java library at GrepCode.com. Example: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Integer.java#Integer.highestOneBit%28int%29 – kevinarpe Mar 07 '16 at 11:03

3 Answers3

8

This site provides an implementation that should work in C# with a few modifications:

public static uint highestOneBit(uint i) 
{
    i |= (i >>  1);
    i |= (i >>  2);
    i |= (i >>  4);
    i |= (i >>  8);
    i |= (i >>  16);
    return i - (i >> 1);
}

http://ideone.com/oEiNcM

It basically fills all bit places lower than the highest one with 1s and then removes all except the highest bit.

Example (using only 16 bits instead of 32):

start: i =        0010000000000000
i |= (i >> 1)     0010000000000000 | 0001000000000000 -> 0011000000000000
i |= (i >> 2)     0011000000000000 | 0000110000000000 -> 0011110000000000
i |= (i >> 4)     0011110000000000 | 0000001111000000 -> 0011111111000000
i |= (i >> 8)     0011111111000000 | 0000000000111111 -> 0011111111111111
i - (i >> 1)      0011111111111111 - 0001111111111111 -> 0010000000000000
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • Just tested the output of this method against @Pierre-Luc's answer. It remains the same. So I guess this too is a valid implementation. – Nathan Mar 04 '15 at 05:05
  • On a long run this would be the ideal way to go about. I have added this function as an overload for dealing with highestOneBit operation on uints. – Nathan Mar 04 '15 at 05:26
  • Why only `i |= (i >> 8);` it won't work for values larger than 16 bits with a zero byte like 0x02300000 – phuclv Mar 04 '15 at 05:53
  • 1
    @LưuVĩnhPhúc My mistake. I was mistakenly thinking that ints were 16 bits. – JLRishe Mar 04 '15 at 06:12
2

You can write your own Extension method for the int type:

public static class Extensions
{
    public static int HighestOneBit(this int number)
    {
        return (int)Math.Pow(2, Convert.ToString(number, 2).Length - 1);
    }
}

so it can be used as

int number = 170;
int result = number.HighestOneBit(); //128

or directly

int result = 170.HighestOneBit(); //128

Here's how it works:

The ToString(number, 2) writes our number in binary form (ex: 10101010 for 170). We then use the first bit position (the length - 1) to calculate the value of 2^(first bit position), which is 128 in this case. Finally, since Math.Pow returns a double, we downcast it to int.

Pierre-Luc Pineault
  • 8,993
  • 6
  • 40
  • 55
  • 1
    This is a really expensive way to carry out an operation on bits. – JLRishe Mar 04 '15 at 05:00
  • @JLRishe Yes, it is. But at least you don't have to convert back and forth from `uints`. And unless it's performed more than 10 million times in a row, the difference is not really noticeable. – Pierre-Luc Pineault Mar 04 '15 at 05:07
  • But I thought bit shifts were expensive too :( – Nathan Mar 04 '15 at 05:07
  • 1
    @AmatuerDev Those bit shifts are about 17 times less expensive, on a 5M iterations benchmark. It get noticeable at around 100k iterations, where it can take up to a couple milliseconds to execute. – Pierre-Luc Pineault Mar 04 '15 at 05:09
  • @Pierre-LucPineault If you're working with bits, you generally should be working with unsigned values. My answer could be written with `int` instead of `uint` just fine, but it would produce incorrect results for values that use the 16th bit (and your currently _does_ produce incorrect results for such values). – JLRishe Mar 04 '15 at 05:16
  • @AmatuerDev Bitwise operations are in general very inexpensive. – JLRishe Mar 04 '15 at 05:16
  • bit shifts can run in a single cycle while logs may need tens or hundreds of cycles – phuclv Mar 04 '15 at 05:20
  • @LưuVĩnhPhúc There's no log here, but it involves int -> string conversion and up to 16 floating point multiplication operations. – JLRishe Mar 04 '15 at 05:22
  • Ahh I guess my assumption about bit shift was wrong after all. But since I need to call this function just twice in a run. I guess I shouldn't be much bothered about performance impact. – Nathan Mar 04 '15 at 05:24
  • oops I thought it was a log version before. But anyway conversion is still much expensive since it involves multiplication – phuclv Mar 04 '15 at 05:24
  • @JLRishe Incorrect results for the 16th bit? Don't you mean 32nd? Works fine up to `int.max`, and OP is working with ints. If you have examples of those incorrect results, I'd like to see them. – Pierre-Luc Pineault Mar 04 '15 at 05:29
2

Update: .NET Core 3.0 introduced BitOperations.LeadingZeroCount() and BitOperations.Log2() which map directly to the underlying CPU's bitwise leading zero count instruction, hence extremely efficient

public static uint highestOneBit(uint i) 
{
    return i == 0 ? 0 : 1 << BitOperations.Log2(i); // or
    // return i == 0 ? 0 : 1 << (31 - BitOperations.LeadingZeroCount(i));
}

This is basically round down to the next power of 2 and there are so many ways to do that in the famous bithacks site. The implementations there is for rounding up to the next power of 2 so just shift right by 1 to get what you want

public static uint highestOneBit(uint i) 
{
   v--;
   v |= v >> 1;
   v |= v >> 2;
   v |= v >> 4;
   v |= v >> 8;
   v |= v >> 16;
   v++;      // now v is the next power of 2
   v >>= 1;  // get the previous power of 2
}

Another way:

public static uint highestOneBit(uint v) 
{
   const int MultiplyDeBruijnBitPosition[32] = 
   {
      0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
      8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
   };

   v |= v >> 1;
   v |= v >> 2;
   v |= v >> 4;
   v |= v >> 8;
   v |= v >> 16;

   return 1 << MultiplyDeBruijnBitPosition[(v*0x07C4ACDDU) >> 27];
}
phuclv
  • 37,963
  • 15
  • 156
  • 475