There are a lot of questions about this on StackOverflow. A lot. However I cannot find an answer that:
Faster than:
private static int Obvious(ulong v)
{
int r = 0;
while ((v >>= 1) != 0)
{
r++;
}
return r;
}
Or even
int r = (int)(Math.Log(v,2));
I'm assuming a 64-bit Intel CPU here.
One useful reference is the Bit Hacks page and another is fxtbook.pdf However, while these gives useful direction to approach the problem, they do not give a ready answer.
I'm after a re-usable function that can do something similar to _BitScanForward64 and _BitScanReverse64 only for C#.
In a binary number, the bit furthest to the left is called the most significant bit (msb) and the bit furthest to the right is called the least significant bit (lsb). The MSB gives the sign of the number (sign bit) , 0 for positive and 1 for negative.
The value at the least significant bit position = x & 1. The value of the isolated least significant 1 = x & -x. The zero-based index of the isolated least significant 1 = log2(x & -x)
Given a number, find the greatest number less than the given a number which is the power of two or find the most significant bit number .
The ffs() function returns the position of the first (least significant) bit set in the word i. The least significant bit is position 1 and the most significant position e.g. 32 or 64.
.NET Core 3.0 added BitOperations.LeadingZeroCount and BitOperations.TrailingZeroCount so you can use them directly. They'll be mapped to the x86's LZCNT/BSR and TZCNT/BSF instructions, hence extremely efficient
int mostSignificantPosition = 63 - BitOperations.LeadingZeroCount(0x1234L);
int leastSignificantPosition = BitOperations.TrailingZeroCount(0x1234L);
Alternatively the most significant bit's position can be calculated like this
int mostSignificantPosition = BitOperations.Log2(x - 1) + 1
One of the ways of doing this, that is described on the Bit Hacks page linked in the question is leveraging De Bruijn sequence. Unfortunately this page does not give a 64-bit version of said sequence. This useful page explains how De Bruijn sequences can be constructed, and this one gives an example of the sequence generator written in C++. If we adapt the given code we can generated multiple sequences, one of which is given in the C# code below:
public static class BitScanner
{
private const ulong Magic = 0x37E84A99DAE458F;
private static readonly int[] MagicTable =
{
0, 1, 17, 2, 18, 50, 3, 57,
47, 19, 22, 51, 29, 4, 33, 58,
15, 48, 20, 27, 25, 23, 52, 41,
54, 30, 38, 5, 43, 34, 59, 8,
63, 16, 49, 56, 46, 21, 28, 32,
14, 26, 24, 40, 53, 37, 42, 7,
62, 55, 45, 31, 13, 39, 36, 6,
61, 44, 12, 35, 60, 11, 10, 9,
};
public static int BitScanForward(ulong b)
{
return MagicTable[((ulong) ((long) b & -(long) b)*Magic) >> 58];
}
public static int BitScanReverse(ulong b)
{
b |= b >> 1;
b |= b >> 2;
b |= b >> 4;
b |= b >> 8;
b |= b >> 16;
b |= b >> 32;
b = b & ~(b >> 1);
return MagicTable[b*Magic >> 58];
}
}
I also posted my C# port of the sequence generator to github
Another related article not mentioned in the question with decent cover of De Bruijn sequences, can be found here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With