clang and GCC have a int __builtin_ctz(unsigned)
function. This counts the trailing zeros in an integer. The Wikipedia article on this family of functions mentions that the binary GCD algorithm can be sped up using __builtin_ctz
, but I don't understand how.
The sample implementation of the binary GCD looks like this:
unsigned int gcd(unsigned int u, unsigned int v)
{
// simple cases (termination)
if (u == v)
return u;
if (u == 0)
return v;
if (v == 0)
return u;
// look for factors of 2
if (~u & 1) // u is even
if (v & 1) // v is odd
return gcd(u >> 1, v);
else // both u and v are even
return gcd(u >> 1, v >> 1) << 1;
if (~v & 1) // u is odd, v is even
return gcd(u, v >> 1);
// reduce larger argument
if (u > v)
return gcd(u - v, v);
return gcd(v - u, u);
}
My suspicion is that I could use __builtin_ctz
as follows:
constexpr unsigned int gcd(unsigned int u, unsigned int v)
{
// simplified first three ifs
if (u == v || u == 0 || v == 0)
return u | v;
unsigned ushift = __builtin_ctz(u);
u >>= ushift;
unsigned vshift = __builtin_ctz(v);
v >>= vshift;
// Note sure if max is the right approach here.
// In the if-else block you can see both arguments being rshifted
// and the result being leftshifted only once.
// I expected to recreate this behavior using max.
unsigned maxshift = std::max(ushift, vshift);
// The only case which was not handled in the if-else block before was
// the odd/odd case.
// We can detect this case using the maximum shift.
if (maxshift != 0) {
return gcd(u, v) << maxshift;
}
return (u > v) ? gcd(u - v, v) : gcd(v - u, u);
}
int main() {
constexpr unsigned result = gcd(5, 3);
return result;
}
Unfortunately this does not work yet. The program results in 4, when it should be 1. So what am I doing wrong? How can I use __builtin_ctz
correctly here? See my code so far on GodBolt.
min
where you havemax
, but it works a bit different overall – Stitesstd::gcd
? Is this supposed to be faster? – Discourteousgcd
. It goes into a very deep recursion @u=3508125240
,v=2952784951
. Here are themax
,min
andshift
values. – Discourteousstd::gcd
and BrettHale's is even faster than that. (see my answer for the benchmark results) – Dentstd::gcd
. – Discourteous