1.0.20.31: tweaking LOG
authorNikodemus Siivola <nikodemus@random-state.net>
Fri, 26 Sep 2008 16:24:01 +0000 (16:24 +0000)
committerNikodemus Siivola <nikodemus@random-state.net>
Fri, 26 Sep 2008 16:24:01 +0000 (16:24 +0000)
 * In case of (LOG INTEGER DOUBLE) and (LOG DOUBLE INTEGER), don't use
   intermediate single precision values.

 * Fix unoptimized (LOG X 0.0d0) => 0.0d0, and (LOG DOUBLE 0) => 0.0d0
   (both were 0.0f0).

NEWS
src/code/irrat.lisp
tests/float.pure.lisp
version.lisp-expr

diff --git a/NEWS b/NEWS
index 18ffd73..daf653c 100644 (file)
--- a/NEWS
+++ b/NEWS
     owned by other threads anymore.
   * bug fix: FIND on lists called KEY outside the specified
     subsequence. (reported by budden)
+  * bug fix: LOG doesn't use single-float intermediate results when
+    given mixed integer and double-float arguments, leading to better
+    precision. (reported by Bob Felts)
+  * bug fix: LOG with base zero returned values of inconsistent type.
 
 changes in sbcl-1.0.20 relative to 1.0.19:
   * minor incompatible change: OPTIMIZE qualities
index 65c96f1..79be34a 100644 (file)
   "Return the logarithm of NUMBER in the base BASE, which defaults to e."
   (if base-p
       (cond
-        ((zerop base) 0f0) ; FIXME: type
+        ((zerop base)
+         (if (or (typep number 'double-float) (typep base 'double-float))
+             0.0d0
+             0.0f0))
         ((and (typep number '(integer (0) *))
               (typep base '(integer (0) *)))
          (coerce (/ (log2 number) (log2 base)) 'single-float))
-        (t (/ (log number) (log base))))
+        ((or (and (typep number 'integer) (typep base 'double-float))
+             (and (typep number 'double-float) (typep base 'integer)))
+         ;; No single float intermediate result
+         (/ (log2 number) (log base 2.0d0)))
+        (t
+         (/ (log number) (log base))))
       (number-dispatch ((number number))
         (((foreach fixnum bignum))
          (if (minusp number)
index 7acc048..5140bbd 100644 (file)
       (test (not (> nan nan)))
       (test (not (> -1.0 nan)))
       (test (not (> nan 1.0))))))
+
+(with-test (:name :log-int/double-accuracy)
+  ;; we used to use single precision for intermediate results
+  (assert (eql 2567.6046442221327d0
+               (log (loop for n from 1 to 1000 for f = 1 then (* f n)
+                          finally (return f))
+                    10d0))))
+
+(with-test (:name :log-base-zero-return-type)
+  (assert (eql 0.0f0 (log 123 (eval 0))))
+  (assert (eql 0.0d0 (log 123.0d0 (eval 0))))
+  (assert (eql 0.0d0 (log 123 (eval 0.0d0))))
+  (let ((f (compile nil '(lambda (x y)
+                          (declare (optimize speed))
+                          (etypecase x
+                            (single-float
+                             (etypecase y
+                               (single-float (log x y))
+                               (double-float (log x y))))
+                            (double-float
+                             (etypecase y
+                               (single-float (log x y))
+                               (double-float (log x y)))))))))
+    (assert (eql 0.0f0 (funcall f 123.0 0.0)))
+    (assert (eql 0.0d0 (funcall f 123.0d0 0.0)))
+    (assert (eql 0.0d0 (funcall f 123.0d0 0.0d0)))
+    (assert (eql 0.0d0 (funcall f 123.0 0.0d0)))))
index 8402b33..30863b0 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.20.30"
+"1.0.20.31"