0.8.15.15: Removing non-ANSI FTYPE proclaims and TYPE declarares from PCL
[sbcl.git] / src / code / float.lisp
index d5ea0b9..29cefa3 100644 (file)
 ;;; Scale a single or double float, calling the correct over/underflow
 ;;; functions.
 (defun scale-single-float (x exp)
-  (declare (single-float x) (fixnum exp))
-  (let* ((bits (single-float-bits x))
-        (old-exp (ldb sb!vm:single-float-exponent-byte bits))
-        (new-exp (+ old-exp exp)))
-    (cond
-     ((zerop x) x)
-     ((or (< old-exp sb!vm:single-float-normal-exponent-min)
-         (< new-exp sb!vm:single-float-normal-exponent-min))
-      (scale-float-maybe-underflow x exp))
-     ((or (> old-exp sb!vm:single-float-normal-exponent-max)
-         (> new-exp sb!vm:single-float-normal-exponent-max))
-      (scale-float-maybe-overflow x exp))
-     (t
-      (make-single-float (dpb new-exp
-                             sb!vm:single-float-exponent-byte
-                             bits))))))
+  (declare (single-float x) (integer exp))
+  (etypecase exp
+    (fixnum
+     (let* ((bits (single-float-bits x))
+           (old-exp (ldb sb!vm:single-float-exponent-byte bits))
+           (new-exp (+ old-exp exp)))
+       (cond
+        ((zerop x) x)
+        ((or (< old-exp sb!vm:single-float-normal-exponent-min)
+             (< new-exp sb!vm:single-float-normal-exponent-min))
+         (scale-float-maybe-underflow x exp))
+        ((or (> old-exp sb!vm:single-float-normal-exponent-max)
+             (> new-exp sb!vm:single-float-normal-exponent-max))
+         (scale-float-maybe-overflow x exp))
+        (t
+         (make-single-float (dpb new-exp
+                                 sb!vm:single-float-exponent-byte
+                                 bits))))))
+    (unsigned-byte (scale-float-maybe-overflow x exp))
+    ((integer * 0) (scale-float-maybe-underflow x exp))))
 (defun scale-double-float (x exp)
-  (declare (double-float x) (fixnum exp))
-  (let* ((hi (double-float-high-bits x))
-        (lo (double-float-low-bits x))
-        (old-exp (ldb sb!vm:double-float-exponent-byte hi))
-        (new-exp (+ old-exp exp)))
-    (cond
-     ((zerop x) x)
-     ((or (< old-exp sb!vm:double-float-normal-exponent-min)
-         (< new-exp sb!vm:double-float-normal-exponent-min))
-      (scale-float-maybe-underflow x exp))
-     ((or (> old-exp sb!vm:double-float-normal-exponent-max)
-         (> new-exp sb!vm:double-float-normal-exponent-max))
-      (scale-float-maybe-overflow x exp))
-     (t
-      (make-double-float (dpb new-exp sb!vm:double-float-exponent-byte hi)
-                        lo)))))
+  (declare (double-float x) (integer exp))
+  (etypecase exp
+    (fixnum
+     (let* ((hi (double-float-high-bits x))
+           (lo (double-float-low-bits x))
+           (old-exp (ldb sb!vm:double-float-exponent-byte hi))
+           (new-exp (+ old-exp exp)))
+       (cond
+        ((zerop x) x)
+        ((or (< old-exp sb!vm:double-float-normal-exponent-min)
+             (< new-exp sb!vm:double-float-normal-exponent-min))
+         (scale-float-maybe-underflow x exp))
+        ((or (> old-exp sb!vm:double-float-normal-exponent-max)
+             (> new-exp sb!vm:double-float-normal-exponent-max))
+         (scale-float-maybe-overflow x exp))
+        (t
+         (make-double-float (dpb new-exp sb!vm:double-float-exponent-byte hi)
+                            lo)))))
+    (unsigned-byte (scale-float-maybe-overflow x exp))
+    ((integer * 0) (scale-float-maybe-underflow x exp))))
 
 #!+(and x86 long-float)
 (defun scale-long-float (x exp)
-  (declare (long-float x) (fixnum exp))
+  (declare (long-float x) (integer exp))
   (scale-float x exp))
 
 ;;; Dispatch to the correct type-specific scale-float function.
@@ -779,6 +787,13 @@ uninterruptibly frob the rounding modes & do ieee round-to-integer.
                 (- rounded)
                 rounded)))))))
 
+(defun %unary-ftruncate (number)
+  (number-dispatch ((number real))
+    ((integer) (float number))
+    ((ratio) (float (truncate (numerator number) (denominator number))))
+    (((foreach single-float double-float #!+long-float long-float))
+     (%unary-ftruncate number))))
+
 (defun rational (x)
   #!+sb-doc
   "RATIONAL produces a rational number for any real numeric argument. This is
@@ -797,41 +812,108 @@ uninterruptibly frob the rounding modes & do ieee round-to-integer.
                 (integer-/-integer (ash int ex) (ash 1 digits)))))))
     ((rational) x)))
 
+;;; This algorithm for RATIONALIZE, due to Bruno Haible, is included
+;;; with permission.
+;;;
+;;; Algorithm (recursively presented):
+;;;   If x is a rational number, return x.
+;;;   If x = 0.0, return 0.
+;;;   If x < 0.0, return (- (rationalize (- x))).
+;;;   If x > 0.0:
+;;;     Call (integer-decode-float x). It returns a m,e,s=1 (mantissa,
+;;;     exponent, sign).
+;;;     If m = 0 or e >= 0: return x = m*2^e.
+;;;     Search a rational number between a = (m-1/2)*2^e and b = (m+1/2)*2^e
+;;;     with smallest possible numerator and denominator.
+;;;     Note 1: If m is a power of 2, we ought to take a = (m-1/4)*2^e.
+;;;       But in this case the result will be x itself anyway, regardless of
+;;;       the choice of a. Therefore we can simply ignore this case.
+;;;     Note 2: At first, we need to consider the closed interval [a,b].
+;;;       but since a and b have the denominator 2^(|e|+1) whereas x itself
+;;;       has a denominator <= 2^|e|, we can restrict the seach to the open
+;;;       interval (a,b).
+;;;     So, for given a and b (0 < a < b) we are searching a rational number
+;;;     y with a <= y <= b.
+;;;     Recursive algorithm fraction_between(a,b):
+;;;       c := (ceiling a)
+;;;       if c < b
+;;;         then return c       ; because a <= c < b, c integer
+;;;         else
+;;;           ; a is not integer (otherwise we would have had c = a < b)
+;;;           k := c-1          ; k = floor(a), k < a < b <= k+1
+;;;           return y = k + 1/fraction_between(1/(b-k), 1/(a-k))
+;;;                             ; note 1 <= 1/(b-k) < 1/(a-k)
+;;;
+;;; You can see that we are actually computing a continued fraction expansion.
+;;;
+;;; Algorithm (iterative):
+;;;   If x is rational, return x.
+;;;   Call (integer-decode-float x). It returns a m,e,s (mantissa,
+;;;     exponent, sign).
+;;;   If m = 0 or e >= 0, return m*2^e*s. (This includes the case x = 0.0.)
+;;;   Create rational numbers a := (2*m-1)*2^(e-1) and b := (2*m+1)*2^(e-1)
+;;;   (positive and already in lowest terms because the denominator is a
+;;;   power of two and the numerator is odd).
+;;;   Start a continued fraction expansion
+;;;     p[-1] := 0, p[0] := 1, q[-1] := 1, q[0] := 0, i := 0.
+;;;   Loop
+;;;     c := (ceiling a)
+;;;     if c >= b
+;;;       then k := c-1, partial_quotient(k), (a,b) := (1/(b-k),1/(a-k)),
+;;;            goto Loop
+;;;   finally partial_quotient(c).
+;;;   Here partial_quotient(c) denotes the iteration
+;;;     i := i+1, p[i] := c*p[i-1]+p[i-2], q[i] := c*q[i-1]+q[i-2].
+;;;   At the end, return s * (p[i]/q[i]).
+;;;   This rational number is already in lowest terms because
+;;;   p[i]*q[i-1]-p[i-1]*q[i] = (-1)^i.
+;;;
+;;; See also
+;;;   Hardy, Wright: An introduction to number theory
+;;; and/or
+;;;   <http://modular.fas.harvard.edu/edu/Fall2001/124/lectures/lecture17/lecture17/>
+;;;   <http://modular.fas.harvard.edu/edu/Fall2001/124/lectures/lecture17/lecture18/>
+
 (defun rationalize (x)
-  #!+sb-doc
-  "Converts any REAL to a RATIONAL. Floats are converted to a simple rational
+  "Converts any REAL to a RATIONAL.  Floats are converted to a simple rational
   representation exploiting the assumption that floats are only accurate to
-  their precision. RATIONALIZE (and also RATIONAL) preserve the invariant:
+  their precision.  RATIONALIZE (and also RATIONAL) preserve the invariant:
       (= x (float (rationalize x) x))"
   (number-dispatch ((x real))
     (((foreach single-float double-float #!+long-float long-float))
-     ;; Thanks to Kim Fateman, who stole this function rationalize-float from
-     ;; macsyma's rational. Macsyma'a rationalize was written by the legendary
-     ;; Gosper (rwg). Guy Steele said about Gosper, "He has been called the
-     ;; only living 17th century mathematician and is also the best pdp-10
-     ;; hacker I know." So, if you can understand or debug this code you win
-     ;; big.
-     (cond ((minusp x) (- (rationalize (- x))))
-          ((zerop x) 0)
-          (t
-           (let ((eps (etypecase x
-                          (single-float single-float-epsilon)
-                          (double-float double-float-epsilon)
-                          #!+long-float
-                          (long-float long-float-epsilon)))
-                 (y ())
-                 (a ()))
-             (do ((xx x (setq y (/ (float 1.0 x) (- xx (float a x)))))
-                  (num (setq a (truncate x))
-                       (+ (* (setq a (truncate y)) num) onum))
-                  (den 1 (+ (* a den) oden))
-                  (onum 1 num)
-                  (oden 0 den))
-                 ((and (not (zerop den))
-                       (not (> (abs (/ (- x (/ (float num x)
-                                               (float den x)))
-                                       x))
-                               eps)))
-                  (integer-/-integer num den))
-               (declare ((dispatch-type x) xx)))))))
+     ;; This is a fairly straigtforward implementation of the
+     ;; iterative algorithm above.
+     (multiple-value-bind (frac expo sign)
+        (integer-decode-float x)
+       (cond ((or (zerop frac) (>= expo 0))
+             (if (minusp sign)
+                 (- (ash frac expo))
+                 (ash frac expo)))
+            (t
+             ;; expo < 0 and (2*m-1) and (2*m+1) are coprime to 2^(1-e),
+             ;; so build the fraction up immediately, without having to do
+             ;; a gcd.
+             (let ((a (build-ratio (- (* 2 frac) 1) (ash 1 (- 1 expo))))
+                   (b (build-ratio (+ (* 2 frac) 1) (ash 1 (- 1 expo))))
+                   (p0 0)
+                   (q0 1)
+                   (p1 1)
+                   (q1 0))
+               (do ((c (ceiling a) (ceiling a)))
+                   ((< c b)
+                    (let ((top (+ (* c p1) p0))
+                          (bot (+ (* c q1) q0)))
+                      (build-ratio (if (minusp sign)
+                                       (- top)
+                                       top)
+                                   bot)))
+                 (let* ((k (- c 1))
+                        (p2 (+ (* k p1) p0))
+                        (q2 (+ (* k q1) q0)))
+                   (psetf a (/ (- b k))
+                          b (/ (- a k)))
+                   (setf p0 p1
+                         q0 q1
+                         p1 p2
+                         q1 q2))))))))
     ((rational) x)))