问题
I am trying to find an optimal code to locate a single bit index in a long integer (64 bit). The long integer has exactly one set bit. (Using C language)
Currently, I am just shifting the whole thing by one bit, then checking for zero. I have read about lookup tables, but it won't do for the whole 64 bits. I thought about checking each 8 bits for zero, if not use a lookup, but still I'll have to shift by 8 at a time. (Shifting by 8 is better than shifting by one 8 times?)
(Note: I am developing for mobile devices, and they are [not surprisingly] slow).
回答1:
Whenever I need some way to manipulate bits, I always look for Bit Twiddling Hacks. It has few solutions for your problem as well.
This solution seems to be fast and most advanced:
Count the consecutive zero bits (trailing) on the right in parallel
unsigned int v; // 32-bit word input to count zero bits on right
unsigned int c = 32; // c will be the number of zero bits on the right
v &= -signed(v);
if (v) c--;
if (v & 0x0000FFFF) c -= 16;
if (v & 0x00FF00FF) c -= 8;
if (v & 0x0F0F0F0F) c -= 4;
if (v & 0x33333333) c -= 2;
if (v & 0x55555555) c -= 1;
The number of operations is at most 3 * lg(N) + 4, roughly, for N bit words.
回答2:
You can do a binary search for the bit that's set:
int bitn(unsigned long long x)
{
int n = 0;
if (x >> 32) {
n += 32;
x >>= 32;
}
if (x >> 16) {
n += 16;
x >>= 16;
}
if (x >> 8) {
n += 8;
x >>= 8;
}
if (x >> 4) {
n += 4;
x >>= 4;
}
if (x >> 2) {
n += 2;
x >>= 2;
}
if (x >> 1) {
n += 1;
}
return n;
}
GCC provides a builtin, __builtin_ctzll()
, that performs this function (which will take advantage of any special functionality that the hardware might have to do this quickly).
回答3:
You should check this suggestion (and any others given here) against your current code - you may find that the bitshifting is the most efficient way, or that the difference is minuscule in which case you should optimise for readability.
In any case, consider this something to try and benchmark rather than something that's guaranteed to be faster.
Since there are only 64 possible values, you can just use something like:
int getSetBit (unsigned long x) {
if (x == 0x8000000000000000UL) return 63;
if (x == 0x4000000000000000UL) return 62;
if (x == 0x2000000000000000UL) return 61;
if (x == 0x1000000000000000UL) return 60;
if (x == 0x0800000000000000UL) return 59;
if (x == 0x0400000000000000UL) return 58;
:
if (x == 0x0000000000000002UL) return 1;
return 0;
}
You may find that's faster but solutions will generally be affected by a whole lot of things outside the scope of the standard (optimisation strategies, data caching, pipelining and so forth).
If you're willing to move away from Standard C, many environments will have optimised things you can use, such as the gcc
:
int __builtin_ffs (unsigned int x)
// Returns one plus the index of the least significant
// 1-bit of x, or if x is zero, returns zero.
Of course, with that one, you may have to split the long
into two int
types and check each one individually, something like (untested but you should get the general idea):
if (x < 0x80000000UL) return __builtin_ffs((unsigned int)x) - 1;
return __builtin_ffs((unsigned int)(x>>32)) -1 + 32;
Alternatively, the output from __builtin_clzl()
can be manipulated to give you the bit position (it gives you the leading zero count) and it works with unsigned long
already. You can see the gcc
built-ins here.
来源:https://stackoverflow.com/questions/14997894/index-of-single-bit-in-long-integer-in-c