Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast way of finding most and least significant bit set in a 64-bit integer

There are a lot of questions about this on StackOverflow. A lot. However I cannot find an answer that:

  • Works in C#
  • Works for 64-bit integers (as opposed to 32-bit)

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#.

like image 231
Andrew Savinykh Avatar asked Jul 13 '15 02:07

Andrew Savinykh


People also ask

How do you find most significant bit and least significant bit?

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.

How do you find the least significant bit of integers?

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)

How do you find the most significant bit of a number?

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 .

How do you find the position of the least significant bit?

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.


Video Answer


2 Answers

.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
like image 124
phuclv Avatar answered Sep 23 '22 09:09

phuclv


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.

like image 33
Andrew Savinykh Avatar answered Sep 23 '22 09:09

Andrew Savinykh