Determining the position of the highest set bit?
- hairy_pete
- Baaaaaaa!

- Posts: 7217
- Joined: Mon May 27, 2002 8:10 pm 0
- Location: A Tiny Village in Bavaria
- Kemper_Boyd
- Sheepie Whore

- Posts: 11967
- Joined: Wed Jul 31, 2002 9:15 pm 0
That's more or less my assertion. Either they use something very fast likehairy_pete wrote:My guess is that a BSF sort of does exactly that in microcode, which inherently would be faster than doing it in macrocode - if only due to the effects you mentioned. Just wanted to demonstrate
popcount, or they have near optimal uOPs from ROM. There may be
cases other than the general where clever code can still win.
Re: Determining the position of the highest set bit?
[quote="Dvorak"]Does anyone know of a fast way of determining the position of the highest set bit in a 32-bit value?
I'm actually using a loop like this:
[code]for (count=32; bits; bits>>=1)
count--;
[/code]
That is, shifting the value to be examined until it vanishes and counting the necessary steps.
Any ideas?
x86 assembler welcome again.
[/quote]
The fastiest way is:
if Y = 2^X then:
[code]
[b]
{
X = 0;
if (Y & 0xFFFF0000) X |= 0x10;
if (Y & 0xFF00FF00) X |= 0x08;
if (Y & 0xF0F0F0F0) X |= 0x04;
if (Y & 0xCCCCCCCC) X |= 0x02;
if (Y & 0xAAAAAAAA) X |= 0x01;
}[/b][/code]
Ehud
I'm actually using a loop like this:
[code]for (count=32; bits; bits>>=1)
count--;
[/code]
That is, shifting the value to be examined until it vanishes and counting the necessary steps.
Any ideas?
x86 assembler welcome again.
The fastiest way is:
if Y = 2^X then:
[code]
[b]
{
X = 0;
if (Y & 0xFFFF0000) X |= 0x10;
if (Y & 0xFF00FF00) X |= 0x08;
if (Y & 0xF0F0F0F0) X |= 0x04;
if (Y & 0xCCCCCCCC) X |= 0x02;
if (Y & 0xAAAAAAAA) X |= 0x01;
}[/b][/code]
Ehud
Re: Determining the position of the highest set bit?
Nice try, and along the right lines. However, you don't deal with the no bits set case.The fastiest way is:
if Y = 2^X then:
EhudCode: Select all
[b] { X = 0; if (Y & 0xFFFF0000) X |= 0x10; if (Y & 0xFF00FF00) X |= 0x08; if (Y & 0xF0F0F0F0) X |= 0x04; if (Y & 0xCCCCCCCC) X |= 0x02; if (Y & 0xAAAAAAAA) X |= 0x01; }[/b]
This does (ripped from some code I wrote a while back).
Code: Select all
int y = (((x & 0xffff0000) && 1) << 4) |
(((x & 0xFF00FF00) && 1) << 3) |
(((x & 0xF0F0F0F0) && 1) << 2) |
(((x & 0xCCCCCCCC) && 1) << 1) |
((x & 0xAAAAAAAA) && 1) +
(x && 1);
Re: Determining the position of the highest set bit?
2nd code will be noticable faster than the 1st method shown...tufty wrote:Nice try, and along the right lines. However, you don't deal with the no bits set case.The fastiest way is:
if Y = 2^X then:
EhudCode: Select all
[b] { X = 0; if (Y & 0xFFFF0000) X |= 0x10; if (Y & 0xFF00FF00) X |= 0x08; if (Y & 0xF0F0F0F0) X |= 0x04; if (Y & 0xCCCCCCCC) X |= 0x02; if (Y & 0xAAAAAAAA) X |= 0x01; }[/b]
This does (ripped from some code I wrote a while back).
SimonCode: Select all
int y = (((x & 0xffff0000) && 1) << 4) | (((x & 0xFF00FF00) && 1) << 3) | (((x & 0xF0F0F0F0) && 1) << 2) | (((x & 0xCCCCCCCC) && 1) << 1) | ((x & 0xAAAAAAAA) && 1) + (x && 1);
The first code uses branches (ifs) which will never be the fastest way due to branch prediction misses & the associated pipeline flushes that happen. A problem that gets worse as processors get faster.
The second neatly avoid this because the result of the comparisons are used numerically instead of as a branch.
Taking the snippet: (((x & 0xffff0000) && 1) << 4)
this cunningly avoids the branch, but some compilers may introduce an implicit branch to get the zero bit into a register.
VC doesnt - it (correctly) uses the sbb instruction to achieve that, and produces code that is a mere 27 instruction long with no branches - pretty much as fast as can be. It works out at about 14 cycles with the pentium pipelining too, which is about as long as a single mispredicted branch will take.
Simon =
I think this does the same without using the undocimented features...
It has to calulate the inverse bit pattern then swap it, as the negate trick wont work if the top bit was set. It should work with x being signed or unsigned too ( at least that was the intention
)
It is about 2 cycles slower when compiled though.
I just typed it in off the top of my head so apologies for syntax errors & the like
Code: Select all
int y =( (((int)((unsigned int)x - 0x0000ffff)>>31) & 16) |
(((-(int)(x & 0x00ff00ff))>>31) & 8) |
(((-(int)(x & 0x0F0F0F0F))>>31) & 4) |
(((-(int)(x & 0x33333333))>>31) & 2) |
(((-(int)(x & 0x55555555))>>31) & 1) ) ^ 0x1f;
y += (( (int)(x | (-(int)x))>>31) & 1);
It is about 2 cycles slower when compiled though.
I just typed it in off the top of my head so apologies for syntax errors & the like

