factor: simpler isqrt

function                                             old     new   delta
isqrt_odd                                            102      87     -15

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-04-10 00:24:16 +02:00
parent c804d4ec5c
commit c6476dca54

View File

@ -47,33 +47,37 @@ typedef unsigned long half_t;
/* Returns such x that x+1 > sqrt(N) */ /* Returns such x that x+1 > sqrt(N) */
static inline half_t isqrt(wide_t N) static inline half_t isqrt(wide_t N)
{ {
wide_t x; wide_t mask_2bits;
unsigned c; half_t x;
// Never called with N < 1 // Never called with N < 1
// if (N == 0) // if (N == 0)
// return 0; // return 0;
//
/* Count leading zeros */
c = 0;
while (!(N & TOPMOST_WIDE_BIT)) {
c++;
N <<= 1;
}
N >>= c;
/* Make x > sqrt(n) */ /* First approximation x > sqrt(N) - half as many bits:
x = (wide_t)1 << ((WIDE_BITS + 1 - c) / 2); * 1xxxxx -> 111 (six bits to three)
dbg("x:%llx", x); * 01xxxx -> 111
* 001xxx -> 011
* 0001xx -> 011 and so on.
*/
x = HALF_MAX;
mask_2bits = TOPMOST_WIDE_BIT | (TOPMOST_WIDE_BIT >> 1);
while (mask_2bits && !(N & mask_2bits)) {
x >>= 1;
mask_2bits >>= 2;
}
dbg("x:%"HALF_FMT"x", x);
for (;;) { for (;;) {
wide_t y = (x + N/x) / 2; half_t y = (x + N/x) / 2;
dbg("y:%llx y^2:%llx (y+1)^2:%llx]", y, y*y, (y+1)*(y+1)); dbg("y:%x y^2:%llx", y, (wide_t)y * y);
if (y >= x) { /*
/* Handle degenerate case N = 0xffffffffff...fffffff */ * "real" y may be one bit wider: 0x100000000 and get truncated to 0.
if (y == (wide_t)HALF_MAX + 1) * In this case, "real" y is > x. The first check below is for this case:
y--; */
dbg("isqrt(%llx)=%llx"HALF_FMT, N, y); if (y == 0 || y >= x) {
return y; dbg("isqrt(%llx)=%"HALF_FMT"x", N, x);
return x;
} }
x = y; x = y;
} }
@ -92,6 +96,8 @@ static NOINLINE void factorize(wide_t N)
half_t factor; half_t factor;
half_t max_factor; half_t max_factor;
unsigned count3; unsigned count3;
unsigned count5;
unsigned count7;
if (N < 4) if (N < 4)
goto end; goto end;
@ -103,6 +109,8 @@ static NOINLINE void factorize(wide_t N)
max_factor = isqrt_odd(N); max_factor = isqrt_odd(N);
count3 = 3; count3 = 3;
count5 = 6;
count7 = 9;
factor = 3; factor = 3;
for (;;) { for (;;) {
/* The division is the most costly part of the loop. /* The division is the most costly part of the loop.
@ -123,12 +131,19 @@ static NOINLINE void factorize(wide_t N)
* ^ ^ ^ ^ ^ ^ ^ _ ^ ^ _ ^ ^ ^ ^ * ^ ^ ^ ^ ^ ^ ^ _ ^ ^ _ ^ ^ ^ ^
* (^ = primes, _ = would-be-primes-if-not-divisible-by-5) * (^ = primes, _ = would-be-primes-if-not-divisible-by-5)
*/ */
--count3; count7--;
if (count3 == 0) { count5--;
count3--;
if (count3 && count5 && count7)
continue;
if (count3 == 0)
count3 = 3; count3 = 3;
if (count5 == 0)
count5 = 5;
if (count7 == 0)
count7 = 7;
goto next_factor; goto next_factor;
} }
}
end: end:
if (N > 1) if (N > 1)
printf(" %llu", N); printf(" %llu", N);