Re: integer-length and integer-sqrt Bradley Lucier 07 Oct 2005 10:47 UTC

Jens Axel Søgaard writes:

> I propose adding the following two operations on integers.
>
>
> The first is INTEGER-LENGTH from CLHS
>
>     <http://clhs.lisp.se/Body/f_intege.htm>
>
> which returns the number of bits needed to represent a given
> integer in binary two's-complement format. Although it
> is possible to define INTEGER-LENGTH as a library function,
> it will be more efficient as a primitive, since it can
> exploit the internal representation of an integer.

Agree completely.

> The second is INTEGER-SQRT, which is discussed here:
>
> <http://groups.google.com/group/comp.lang.scheme/browse_frm/thread/
> dc06e91e306cf488/a9cf92f425bb3db1?lnk=st&q=lucier+integer-
> sqrt&rnum=1&hl=en#a9cf92f425bb3db1>
>
> I consider INTEGER-SQRT just as natural an operation as GCD and LCM
> when it comes to number theoretical calculations.

Agree completely.

The following algorithm is better; it's about twice as fast and
Zimmermann has published a proof of correctness:

(define-prim (##exact-int.sqrt x)

   ;; x is non-negative.  Returns (cons s r) where
   ;; x = s^2+r, x < (s+1)^2

   ;; Derived from the paper "Karatsuba Square Root" by Paul Zimmermann,
   ;; INRIA technical report RR-3805, 1999.  (Used in gmp 4.*)

   ;; Note that the statement of the theorem requires that
   ;; b/4 <= high-order digit of x < b which can be impossible when b is
a
   ;; power of 2; the paper later notes that it is the lower bound that
is
   ;; necessary, which we preserve.

   (if (and (##fixnum? x)
            ;; we require that
            ;; (##< (##flonum.sqrt (- (* y y) 1)) y) => #t
            ;; whenever x=y^2 is in this range.  Here we assume that we
            ;; have at least as much precision as IEEE double precision
and
            ;; we round to nearest.
            (or (##not (##fixnum? 4294967296))          ; 32-bit fixnums
                (##fixnum.<= x 4503599627370496)))      ; 2^52
       (let* ((s (##flonum.->fixnum (##flonum.sqrt (##flonum.<-fixnum
x))))
              (r (##fixnum.- x (##fixnum.* s s))))
         (##cons s r))
       (let ((length/4
              (##fixnum.arithmetic-shift-right
               (##fixnum.+ (##integer-length x) 1)
               2)))
         (let* ((s-prime&r-prime
                 (##exact-int.sqrt
                  (##arithmetic-shift
                   x
                   (##fixnum.- (##fixnum.arithmetic-shift-left length/4
1)))))
                (s-prime
                 (##car s-prime&r-prime))
                (r-prime
                 (##cdr s-prime&r-prime)))
           (let* ((qu
                   (##exact-int.div
                    (##+ (##arithmetic-shift r-prime length/4)
                         (##extract-bit-field length/4 length/4 x))
                    (##arithmetic-shift s-prime 1)))
                  (q
                   (##car qu))
                  (u
                   (##cdr qu)))
             (let ((s
                    (##+ (##arithmetic-shift s-prime length/4) q))
                   (r
                    (##- (##+ (##arithmetic-shift u length/4)
                              (##extract-bit-field length/4 0 x))
                         (##* q q))))
               (if (##negative? r)
                 (##cons (##- s 1)
                         (##+ r
                              (##- (##arithmetic-shift s 1) 1)))
                 (##cons s
                         r))))))))