Bit twiddlement question: greater of two popcounts

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

rjgibert
Posts: 317
Joined: Mon Jun 26, 2006 9:44 am

Re: Bit twiddlement question: greater of two popcounts

Post by rjgibert »

mcostalba wrote:Previous post was "almost" :) working.This should do:

Code: Select all

while (a) {
  if(!b) return true;
  a&=a-1; 
  b&=b-1; // skippable if !a
}
return false;
a = 0, b = 0 seems to fail.

In any solution, b should be tested 1st e.g.

Code: Select all

while (b) {
  if(!a) return true;
  a&=a-1; 
  b&=b-1
}
return false;
However, I think this will probably compile to the same code as some of the other working solutions.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Bit twiddlement question: greater of two popcounts

Post by mcostalba »

If popcount <= 2 then

Code: Select all

if&#40;!a&#41; return false;
if&#40;!b&#41; return true;
if&#40;b & &#40;b-1&#41;) return false;
return a & &#40;a-1&#41;;
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Bit twiddlement question: greater of two popcounts

Post by mcostalba »

rjgibert wrote:
mcostalba wrote:Previous post was "almost" :) working.This should do:

Code: Select all

while &#40;a&#41; &#123;
  if&#40;!b&#41; return true;
  a&=a-1; 
  b&=b-1; // skippable if !a
&#125;
return false;
a = 0, b = 0 seems to fail.
?

i understood function should return popcnt(a) > popcnt(b)
Gerd Isenberg
Posts: 2250
Joined: Wed Mar 08, 2006 8:47 pm
Location: Hattingen, Germany

Re: Bit twiddlement question: greater of two popcounts

Post by Gerd Isenberg »

rjgibert wrote:
Gerd Isenberg wrote:
Zach Wegner wrote:What's the fastest way to determine from two sparsely populated bitboards the one with the larger population count?

I was thinking some sort of modified Kernighan style one:

Code: Select all

popcnt_a_ge_b&#40;a,b&#41; &#123;
 while &#40;a!=0 & b!=0&#41; &#123;
  a &= a - 1;
  b &= b - 1;
 &#125;
 return a>=b;
&#125;
At the end of the loop, a or b is zero, so a>=b should be fine. Can this be improved? Seems hard, but you never know...
One minor improvement for the final return, is to reuse one already calculated expression ...

Code: Select all

bool popcnt_a_ge_b&#40;a,b&#41; &#123;
 while &#40;a!=0 & b!=0&#41; &#123;
  a &= a - 1;
  b &= b - 1;
 &#125;
 return a!=0;
&#125;
I believe your code above returns a > b rather than a >= b. Something like the following seems to have been your intent:

Code: Select all

bool popcnt_a_ge_b&#40;a,b&#41; &#123;
 while &#40;1&#41; &#123;
  if &#40;b==0 | a==0&#41; return b==0;
  a &= a - 1;
  b &= b - 1;
 &#125;
&#125;
On the other hand, ZW may find your version perfectly usable as is for his purposes. BTW, I'm working in Python, so I may have screwed up the translation. Apologies in advance if I did.
Yep, you are right.
User avatar
Zach Wegner
Posts: 1922
Joined: Thu Mar 09, 2006 12:51 am
Location: Earth

Re: Bit twiddlement question: greater of two popcounts

Post by Zach Wegner »

Good suggestions everyone, interesting stuff.
mcostalba wrote:If popcnt is less then 4 for each bitboard then multiply for 0x5555... Should do the trick:

return a*0x555... > b*0x555...
Well, as you and Ricardo noticed, that doesn't quite work, but it's a very nice idea. I think it might be able to be used though with a bit more trickery (semi ripped off from CPW):

Code: Select all

bool popcnt_a_ge_b&#40;a,b&#41;
&#123;
    const U64 k1 = C64&#40;0x5555555555555555&#41;;
    a =  a       - (&#40;a >> 1&#41;  & k1&#41;;
    b =  b       - (&#40;b >> 1&#41;  & k1&#41;;
    a *= k1;
    b *= k1;
    return &#40;a >= b&#41;;
&#125;
I think that works. It can of course give errors if either popcnt is >= 4, but I think for a quick SEE it might be worth it. That function is nicely parallel, and branchless. Looks like it should be about 6-7 cycles total. An error here (for popcount>=4) should be pretty rare and can be ignored.

Unfortunately I'm not sure if I need this function at all any more; I might need to just use full popcounts. I'll see what I come up with for this SEE, it's rather tricky.
User avatar
Zach Wegner
Posts: 1922
Joined: Thu Mar 09, 2006 12:51 am
Location: Earth

Re: Bit twiddlement question: greater of two popcounts

Post by Zach Wegner »

rjgibert wrote:
mcostalba wrote:Previous post was "almost" :) working.This should do:

Code: Select all

while &#40;a&#41; &#123;
  if&#40;!b&#41; return true;
  a&=a-1; 
  b&=b-1; // skippable if !a
&#125;
return false;
a = 0, b = 0 seems to fail.

In any solution, b should be tested 1st e.g.

Code: Select all

while &#40;b&#41; &#123;
  if&#40;!a&#41; return true;
  a&=a-1; 
  b&=b-1
&#125;
return false;
However, I think this will probably compile to the same code as some of the other working solutions.
I fear both of these are a bit too branchy. In my original code I used bitwise & to try and avoid the extra branch. Though these are easily predicted, I'd imagine you'd get at least one misprediction for every two calls. Two separate branches make it harder to predict and increase code size a bit.
Pradu
Posts: 287
Joined: Sat Mar 11, 2006 3:19 am
Location: Atlanta, GA

Re: Bit twiddlement question: greater of two popcounts

Post by Pradu »

Zach Wegner wrote:

Code: Select all

bool popcnt_a_ge_b&#40;a,b&#41;
&#123;
    const U64 k1 = C64&#40;0x5555555555555555&#41;;
    a =  a       - (&#40;a >> 1&#41;  & k1&#41;;
    b =  b       - (&#40;b >> 1&#41;  & k1&#41;;
    a *= k1;
    b *= k1;
    return &#40;a >= b&#41;;
&#125;
Very cool! It looks like you could make this into a popcount when number of bits <=4:

Code: Select all

bool popcnt&#40;U64 a&#41;
&#123;
    const U64 k1 = C64&#40;0x5555555555555555&#41;;
    // a*k1 acculumates all the bits into the most significant 2 bits
    // however we want k1*&#40;1<<&#40;an_even_number&#41;) to accumulate
    // as "01" in the most significant 2 bits instead of "10"
    // and therefore subtract &#40;a>>1&#41;&k1 from a
    return ( ( a - (&#40;a>>1&#41;&k1&#41; )*k1 ) >> &#40;64-2&#41;;
&#125;
Perhaps another useful special case of the SWAR popcount is where the number of bits is assumed to be <= 16 ( maybe for rook and bishop mobility? ):

Code: Select all

int popcnt_under16&#40;U64 x&#41;
&#123;
    const U64 k1 = C64&#40;0x1111111111111111&#41;;
    const U64 k3 = C64&#40;0x3333333333333333&#41;;
    const U64 k5 = C64&#40;0x5555555555555555&#41;;
    //accumulate popcnt in every 2 bits
    x -= (&#40;x>>1&#41;&k5&#41;;
    //accumulate in every 4 bits, bring to most-significant 4-bits, 
    //and right-shift the population count back down
    return (( &#40;x&k3&#41; + (&#40;x>>2&#41;&k3&#41; )*k1&#41; >> &#40;64-4&#41;;
&#125;
And perhaps the even less useful 32-bit popcnt:

Code: Select all

int popcnt_under32&#40;U64 x&#41;
&#123;
    const U64 k1  = C64&#40;0x1111111111111111&#41;;
    const U64 k3  = C64&#40;0x3333333333333333&#41;;
    const U64 k5  = C64&#40;0x5555555555555555&#41;;
    const U64 k0F = C64&#40;0x0F0F0F0F0F0F0F0F&#41;;
    const U64 k01 = C64&#40;0x0101010101010101&#41;;

    //accumulate popcnt in every 2 bits
    x -= (&#40;x>>1&#41;&k5&#41;;
    //accumulate in every 4 bits
    x = ( &#40;x&k3&#41; + (&#40;x>>2&#41;&k3&#41; );
    //accumulate in every 8 ...
    return (&#40;x + &#40;x >>4&#41;)&k0F&#41;*k01;
&#125;
Pradu
Posts: 287
Joined: Sat Mar 11, 2006 3:19 am
Location: Atlanta, GA

Re: Bit twiddlement question: greater of two popcounts

Post by Pradu »

Pradu wrote:And perhaps the even less useful 32-bit popcnt:

Code: Select all

int popcnt_under32&#40;U64 x&#41;
&#123;
    const U64 k1  = C64&#40;0x1111111111111111&#41;;
    const U64 k3  = C64&#40;0x3333333333333333&#41;;
    const U64 k5  = C64&#40;0x5555555555555555&#41;;
    const U64 k0F = C64&#40;0x0F0F0F0F0F0F0F0F&#41;;
    const U64 k01 = C64&#40;0x0101010101010101&#41;;

    //accumulate popcnt in every 2 bits
    x -= (&#40;x>>1&#41;&k5&#41;;
    //accumulate in every 4 bits
    x = ( &#40;x&k3&#41; + (&#40;x>>2&#41;&k3&#41; );
    //accumulate in every 8 ...
    return (&#40;x + &#40;x >>4&#41;)&k0F&#41;*k01;
&#125;
Mistake...

Code: Select all

int popcnt_under32&#40;U64 x&#41;
&#123;
    const U64 k1  = C64&#40;0x1111111111111111&#41;;
    const U64 k3  = C64&#40;0x3333333333333333&#41;;
    const U64 k5  = C64&#40;0x5555555555555555&#41;;
    const U64 k0F = C64&#40;0x0F0F0F0F0F0F0F0F&#41;;
    const U64 k01 = C64&#40;0x0101010101010101&#41;;

    //accumulate popcnt in every 2 bits
    x -= (&#40;x>>1&#41;&k5&#41;;
    //accumulate in every 4 bits
    x = ( &#40;x&k3&#41; + (&#40;x>>2&#41;&k3&#41; );
    //accumulate in every 8 ...
    return ((&#40;x + &#40;x >>4&#41;)&k0F&#41;*k01&#41; >> &#40;64-8&#41;;
&#125;
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Bit twiddlement question: greater of two popcounts

Post by mcostalba »

There exsist also 32 bit friendly versions of all of the above, you can look in cpw in case you are interested.