Raymond Toy pushed to branch rtoy-bignum-mult-less-consing at cmucl / cmucl

Commits:

2 changed files:

Changes:

  • src/code/bignum.lisp
    ... ... @@ -884,7 +884,12 @@ down to individual words.")
    884 884
     	  (negate-bignum-in-place result))
    
    885 885
     	(%normalize-bignum result (1+ (* 2 n))))))))
    
    886 886
     
    
    887
    -(defun classical-multiply-bignums (a b)
    
    887
    +;; Bignum multiply using Knuth's algorithm.  We keep this around for
    
    888
    +;; now so we can compare the new algorithm against this to make sure
    
    889
    +;; this are working.
    
    890
    +;;
    
    891
    +;; TODO: Remove this eventually?
    
    892
    +(defun classical-multiply-bignums-knuth (a b)
    
    888 893
       (declare (type bignum-type a b))
    
    889 894
       (let* ((a-plusp (%bignum-0-or-plusp a (%bignum-length a)))
    
    890 895
     	 (b-plusp (%bignum-0-or-plusp b (%bignum-length b)))
    
    ... ... @@ -916,16 +921,29 @@ down to individual words.")
    916 921
         (when negate-res (negate-bignum-in-place res))
    
    917 922
         (%normalize-bignum res len-res)))
    
    918 923
     
    
    919
    -;; Pretend the bignums are actually unsigned, do an unsigned multiply
    
    920
    -;; and then correct the result.  This is based on the algorithm in
    
    921
    -;; Hacker's Delight.
    
    922
    -(defun classical-multiply-bignum-hd (a b)
    
    924
    +;; Classical multiplication of bignums using Knuth's algorithm
    
    925
    +;; modified to handle signed bignums.  Pretend the bignums are
    
    926
    +;; actually unsigned, do an unsigned multiply and then correct the
    
    927
    +;; result.  This is based on the algorithm in Hacker's Delight.
    
    928
    +;;
    
    929
    +;; Let a[n] and b[n] represent the individual bits of each bignum with
    
    930
    +;; M being the number of bits in a and N being the number of bits in
    
    931
    +;; b.  If these are interpreted as an unsigned number, then we are
    
    932
    +;; multiplying numbers
    
    933
    +;;
    
    934
    +;;  (a + 2^M*a[M-1})*(b + 2^N*b[N-1])
    
    935
    +;;    = a*b + 2^M*u[M-1]*b + 2^N*b[N-1]*a + 2^(M+N)*a[M-1]*b[M-1]
    
    936
    +;;
    
    937
    +;; To get the desired result, we need to subtract out the term
    
    938
    +;; 2^M*u[M-1]*b + 2^N*b[N-1]*a from the product.  The last term
    
    939
    +;; doesn't need to subtracted because we know the product fits in M+N
    
    940
    +;; bits and this term is beyond that.
    
    941
    +(defun classical-multiply-bignums (a b)
    
    923 942
       (declare (type bignum-type a b))
    
    924 943
       (let* ((len-a (%bignum-length a))
    
    925 944
     	 (len-b (%bignum-length b))
    
    926 945
     	 (len-res (+ len-a len-b))
    
    927 946
     	 (res (%allocate-bignum len-res)))
    
    928
    -	 (negate-res (not (eq a-plusp b-plusp))))
    
    929 947
         (declare (type bignum-index len-a len-b len-res))
    
    930 948
         ;; Unsigned multiply
    
    931 949
         (dotimes (i len-a)
    
    ... ... @@ -937,36 +955,39 @@ down to individual words.")
    937 955
     		 (type bignum-element-type carry-digit x))
    
    938 956
     	(dotimes (j len-b)
    
    939 957
     	  (multiple-value-bind (big-carry res-digit)
    
    940
    -			       (%multiply-and-add x (%bignum-ref b j)
    
    941
    -						  (%bignum-ref res k)
    
    942
    -						  carry-digit)
    
    958
    +	      (%multiply-and-add x (%bignum-ref b j)
    
    959
    +				 (%bignum-ref res k)
    
    960
    +				 carry-digit)
    
    943 961
     	    (declare (type bignum-element-type big-carry res-digit))
    
    944 962
     	    (setf (%bignum-ref res k) res-digit)
    
    945 963
     	    (setf carry-digit big-carry)
    
    946 964
     	    (incf k)))
    
    947 965
     	(setf (%bignum-ref res k) carry-digit)))
    
    948 966
         ;; Apply corrections if either of the arguments is negative.
    
    949
    -    ;; If a < 0, subtract b*2^M from the result
    
    950
    -    (unless(%bignum-0-or-plusp a)
    
    967
    +    (unless (%bignum-0-or-plusp a len-a)
    
    951 968
           (let ((borrow 1))
    
    952 969
     	(dotimes (j len-b)
    
    970
    +	  (declare (type bignum-index j))
    
    953 971
     	  (let ((index (+ j len-a)))
    
    972
    +	    (declare (type bignum-index index))
    
    954 973
     	    (multiple-value-bind (d borrow-out)
    
    955
    -		(%subtract-with-borrow (bignum-ref res index)
    
    956
    -				       (bignum-ref b j)
    
    974
    +		(%subtract-with-borrow (%bignum-ref res index)
    
    975
    +				       (%bignum-ref b j)
    
    957 976
     				       borrow)
    
    958
    -	      (setf (bignum-ref res index) d)
    
    977
    +	      (setf (%bignum-ref res index) d)
    
    959 978
     	      (setf borrow borrow-out))))))
    
    960
    -    (unless (%bignum-0-or-plusp b)
    
    979
    +    (unless (%bignum-0-or-plusp b len-b)
    
    961 980
           (let ((borrow 1))
    
    962 981
     	(dotimes (j len-a)
    
    982
    +	  (declare (type bignum-index j))
    
    963 983
     	  (let ((index (+ j len-b)))
    
    964
    -	  (multiple-value-bind (d borrow-out)
    
    965
    -	      (%subtract-with-borrow (bignum-ref res index)
    
    966
    -				     (bignum-ref a j)
    
    967
    -				     borrow)
    
    968
    -	    (setf (bignum-ref res index) d)
    
    969
    -	    (setf borrow borrow-out)))))
    
    984
    +	    (declare (type bignum-index index))
    
    985
    +	    (multiple-value-bind (d borrow-out)
    
    986
    +		(%subtract-with-borrow (%bignum-ref res index)
    
    987
    +				       (%bignum-ref a j)
    
    988
    +				       borrow)
    
    989
    +	      (setf (%bignum-ref res index) d)
    
    990
    +	      (setf borrow borrow-out))))))
    
    970 991
         (%normalize-bignum res len-res)))
    
    971 992
     
    
    972 993
     (defparameter *min-karatsuba-bits* 512
    

  • tests/bignum.lisp
    ... ... @@ -21,8 +21,8 @@
    21 21
           (dotimes (k 100)
    
    22 22
     	(let* ((r1 (gen-bignum range (random 2 rng)))
    
    23 23
     	       (r2 (gen-bignum range (random 2 rng)))
    
    24
    -	       (prod-knuth (bignum::classical-multiply-bignums r1 r2))
    
    25
    -	       (prod-hd (bignum::classical-multiply-bignum-hd r1 r2)))
    
    24
    +	       (prod-knuth (bignum::classical-multiply-bignums-knuth r1 r2))
    
    25
    +	       (prod-hd (bignum::classical-multiply-bignums r1 r2)))
    
    26 26
     	  (assert-equal prod-knuth prod-hd r1 r2))))))
    
    27 27
     
    
    28 28
     
    
    ... ... @@ -45,12 +45,12 @@
    45 45
     	     (r2 (gen-bignum range 1)) res)
    
    46 46
     	(time
    
    47 47
     	 (dotimes (k reps)
    
    48
    -	   (declare (fixnum k)) (setf res
    
    49
    -				      (bignum::classical-multiply-bignums r1 r2))))
    
    48
    +	   (declare (fixnum k))
    
    49
    +	   (setf res (bignum::classical-multiply-bignums-knuth r1 r2))))
    
    50 50
     	(print res)
    
    51 51
     	(time
    
    52 52
     	 (dotimes (k reps)
    
    53
    -	   (declare (fixnum k)) (setf res
    
    54
    -				      (bignum::classical-multiply-bignum-hd r1 r2))))
    
    53
    +	   (declare (fixnum k))
    
    54
    +	   (setf res (bignum::classical-multiply-bignums r1 r2))))
    
    55 55
     	(print res)))))
    
    56 56