简体   繁体   中英

fastest way to determine if a bit is set in a integer data type

I have a method that computes a hash value as per some specific algorithm.

uint8_t cal_hash(uint64_t _in_data) {
    uint8_t hash;
    // algorithm
    // bit at hash[0] = XOR of some specific bits in _in_data
    // repeat above statement for other indexed bits of hash 
    return hash;
}

I want to know what could be the most efficient way to access and set corresponding bits in an integer datatype. I have already tried things like

(((x) & (1<<(n)))?1:0)

to determine if a bit is 1 or 0 at any index. Anything better than this ?

Your first concern should be to have a correct and portable version. Compilers nowadays will then optimize such bit operations quite cleverly.

You should always take care that the mask you are using corresponds to the data type that you are testing. Using an int or unsigned might not be enough since you are interested in the bits of a uint64_t and shifting for more than there are bits is undefined behavior.

In your case you would probably use something like

(UINT64_C(1) << (n))

for the mask. If you want to do this in a more generic way you'd have to obtain a 1 of your base type by something like

(1 ? 1U : (X))

alltogether in a macro

#define MASK(X, N) ((1 ? 1U : (X)) << (N))

and then the test could look like

#define BIT(X, N) !!((X) & MASK(X, N))

I think this is a speed vs. memory type question. (Updated with MrSmith42s suggestion):

If you really want speed I would define a mask for each bit a compare that. Perhaps something like:

const uint8_t BitMask[] = { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 };

/* Find out if LSB is set */
if( hash & BitMask[0] ) { ... }

The problem with shifts is that it uses an instruction per shift whereas a fixed mask will only have a single memory acces before the comparison.

For finding a bit that is set quickly, try this:

int oneBit = x & ~(x-1);

After this, oneBit will have ONLY the lowest bit of X set.
(eg, if x was 1011 0100 , oneBit will be 0000 0100 , eg. just the lowest bit)

After that, you can turn off the lowest bit with:

x &= x-1;

(eg: if x was 1011 0100 , new x should be 1011 0000 )

Then you can repeat the first operation to find the next lowest bit that was set.

This has the great advantage that you don't spend time "testing" a bit only to find out that its zero.
It gets you directly to those bits that are set, and skips bits that are zero.

Here's example code that shows it in action:

int main(void)
{
    int x = 180;  // 1011 0100

    while (x)
    {
        printf("Low Bit is: %d\n", x & ~(x-1));
        x &= (x-1);
    }
}

Output:

Low Bit is: 4    // eg. 0000 0100
Low Bit is: 16   // eg. 0001 0000
Low Bit is: 32   // eg. 0010 0000
Low Bit is: 128  // eg. 1000 0000

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM