/* ex: set ts=2 et: */
/*
* Investigate methods of testing whether an integer is a perfect square.
*
* Copyright 2009 Ryan Flynn
* pizza@parseerror.com
* Wed Mar 11 20:53:23 EDT 2009
*
* Ref:
* "Fastest way to determine if an integer's square root is an integer"
*
*/
#include
#include
#include
#include
#include
#include
#if defined(_WIN32) || defined(_WIN64)
typedef unsigned __int64 uint64_t;
# define snprintf _snprintf
#else
# include
#endif
/**
* canonical way to determine if n is a perfect square
*/
static int perfect(const uint64_t n)
{
const uint64_t s = (uint64_t)(sqrt(n) + 0.5);
return s * s == n;
}
/**
* inline asm uses x86 fpu
* - saves call to libm
* - gcc 4.3.3 -O3 generates similar code; but includes redundant call to sqrt() anyways...
*/
static int perfect2(const uint64_t n)
{
int p = 0;
#if defined(_WIN32) || defined(_WIN64)
fprintf(stderr, "write Visual Studio-friendly inline asm!\n");
abort();
#else
__asm__ volatile (
"fildl %2 ;" /* st(0) <- n */
"fld %%st(0) ;" /* st(1) <- st(0) */
"fsqrt ;" /* st(0) <- sqrt(st(0)) */
"frndint ;" /* st(0) <- round(st(0)) */
"fmul %%st(0),%%st(0);" /* st(0) <- st(0) * st(0) */
"fucomip %%st(1),%%st(0);" /* eflags.zf <- st(0) == st(1) */
"sete %%al ;" /* p <- 1 if eflags.zf */
: "=a"(p)
: "a"(p), "m"(n)
);
#endif
return p;
}
static uint64_t parse(const char *s)
{
uint64_t n;
errno = 0;
n = strtoull(s, NULL, 10);
if (errno) {
perror("strtoul");
fprintf(stderr, "use -h for help\n");
exit(1);
}
return n;
}
int main(int argc, char *argv[])
{
uint64_t n,
limit;
if (argc < 2 || 0 == strcmp("-h", argv[1])) {
fprintf(stderr, "Usage: %s [check|speed] \n", argv[0]);
exit(1);
}
if (0 == strcmp(argv[1], "check")) {
/*
* check against canonical for correctness
*/
limit = parse(argv[2]);
printf("checking [0,%llu]...\n", limit);
n = 0;
do
if (perfect2(n) != perfect(n)) {
printf("perfect(%llu)=%d perfect2(%llu)=%d (!)\n",
n, perfect(n), n, perfect2(n));
exit(1);
}
while (n++ != limit);
printf("checked 0-%llu\n", limit-1);
} else if (0 == strcmp(argv[1], "speed")) {
/*
* check a range of input against our fastest candidate for timing purposes
*/
limit = parse(argv[2]);
printf("checking [0,%llu]...\n", limit);
n = 0;
do
(void)perfect2(n);
while (n++ != limit);
printf("done.\n");
} else {
/*
* test a specific number
*/
char test[256];
n = parse(argv[1]);
snprintf(test, sizeof test, "%llu", n);
if (0 != strcmp(test, argv[1])) {
fprintf(stderr, "Shit, strtoull parsed \"%s\" as \"%s\"...\n", argv[1], test);
exit(1);
}
printf("%llu is %sa perfect square\n",
n, (perfect2(n) ? "" : "not "));
}
return 0;
}