isqrt.c 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. /*
  2. * Copyright (C) 2017 Denys Vlasenko <vda.linux@googlemail.com>
  3. *
  4. * Licensed under GPLv2, see file LICENSE in this source tree.
  5. */
  6. //kbuild:lib-y += isqrt.o
  7. #ifndef ISQRT_TEST
  8. # include "libbb.h"
  9. #else
  10. /* gcc -DISQRT_TEST -Wall -O2 isqrt.c -oisqrt && ./isqrt $((RANDOM*RANDOM)) */
  11. # include <stdlib.h>
  12. # include <stdio.h>
  13. # include <time.h>
  14. # define FAST_FUNC /* nothing */
  15. #endif
  16. /* Returns such x that x+1 > sqrt(N) */
  17. unsigned long FAST_FUNC isqrt(unsigned long long N)
  18. {
  19. unsigned long x;
  20. unsigned shift;
  21. #define LL_WIDTH_BITS (unsigned)(sizeof(N)*8)
  22. shift = LL_WIDTH_BITS - 2;
  23. x = 0;
  24. do {
  25. x = (x << 1) + 1;
  26. if ((unsigned long long)x * x > (N >> shift))
  27. x--; /* whoops, that +1 was too much */
  28. shift -= 2;
  29. } while ((int)shift >= 0);
  30. return x;
  31. }
  32. #ifdef ISQRT_TEST
  33. int main(int argc, char **argv)
  34. {
  35. unsigned long long n = argv[1] ? strtoull(argv[1], NULL, 0) : time(NULL);
  36. for (;;) {
  37. unsigned long h;
  38. n--;
  39. h = isqrt(n);
  40. if (!(n & 0xffff))
  41. printf("isqrt(%llx)=%lx\n", n, h);
  42. if ((unsigned long long)h * h > n) {
  43. printf("BAD1: isqrt(%llx)=%lx\n", n, h);
  44. return 1;
  45. }
  46. h++;
  47. if ((unsigned long long)h * h != 0 /* this can overflow to 0 - not a bug */
  48. && (unsigned long long)h * h <= n)
  49. {
  50. printf("BAD2: isqrt(%llx)=%lx\n", n, h);
  51. return 1;
  52. }
  53. }
  54. }
  55. #endif