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))))))))