Change what (log -0.0) returns.

IEEE recommends -inf.
While CLHS has (complex (log (abs x)) (phase x)) as a definition (even
then, ambiguously it says "a complex logarithm"), we already don't
follow a similar definition for sqrt, (sqrt x) = (exp (/ (log x) 2)),
and return (sqrt -0.0) = -0.0

There also had been some confusion around type derivation and (log
(double-float 0d0)) derived as double-float. And an inlined log
produced -inf.
This commit is contained in:
Stas Boukarev 2025-09-12 00:57:10 +03:00
parent 528fdf09a1
commit 3e4bc556ac
4 changed files with 22 additions and 30 deletions

View file

@ -398,23 +398,23 @@
(sqrt number))) (sqrt number)))
(defun log-double-float (number) (defun log-double-float (number)
(if (float-sign-bit-set-p number) (if (< number 0)
(complex (log (- number)) pi) (complex (log (- number)) pi)
(log number))) (log number)))
(defun log-single-float (number) (defun log-single-float (number)
(if (float-sign-bit-set-p number) (if (< number 0)
(complex (log (- number)) (coerce pi 'single-float)) (complex (log (- number)) (coerce pi 'single-float))
(log number))) (log number)))
(defun log-double-float2 (number base) (defun log-double-float2 (number base)
(if (or (float-sign-bit-set-p number) (if (or (< number 0)
(< base 0)) (< base 0))
(/ (log number) (log base)) (/ (log number) (log base))
(truly-the double-float (log number base)))) (truly-the double-float (log number base))))
(defun log-single-float2 (number base) (defun log-single-float2 (number base)
(if (or (float-sign-bit-set-p number) (if (or (< number 0)
(< base 0)) (< base 0))
(/ (log number) (log base)) (/ (log number) (log base))
(truly-the single-float (log number base)))) (truly-the single-float (log number base))))
@ -554,10 +554,8 @@
log2e) log2e)
'single-float))))) 'single-float)))))
(((foreach single-float double-float)) (((foreach single-float double-float))
;; Is (log -0) -infinity (libm.a) or -infinity + i*pi (Kahan)? ;; IEEE 754 says (log -0.0) should be -inf
;; Since this doesn't seem to be an implementation issue (if (< number 0.0)
;; I (pw) take the Kahan result.
(if (float-sign-bit-set-p number)
(complex (log (- number)) (coerce pi '(dispatch-type number))) (complex (log (- number)) (coerce pi '(dispatch-type number)))
(coerce (%log (coerce number 'double-float)) (coerce (%log (coerce number 'double-float))
'(dispatch-type number)))) '(dispatch-type number))))

View file

@ -501,7 +501,7 @@
(deftransform log ((x) ($type) * :node node) (deftransform log ((x) ($type) * :node node)
(let ((cast (cast-or-check-bound-type node (specifier-type 'real)))) (let ((cast (cast-or-check-bound-type node (specifier-type 'real))))
(if cast (if cast
`(if (float-sign-bit-set-p x) `(if (< x 0)
(sb-vm::op-not-type1-error x '(,(type-specifier cast) . log)) (sb-vm::op-not-type1-error x '(,(type-specifier cast) . log))
(truly-the $type (log (truly-the (float 0.0) x)))) (truly-the $type (log (truly-the (float 0.0) x))))
(give-up-ir1-transform)))) (give-up-ir1-transform))))
@ -509,8 +509,7 @@
(deftransform log ((x y) ($type $type) * :node node) (deftransform log ((x y) ($type $type) * :node node)
(let ((cast (cast-or-check-bound-type node (specifier-type 'real)))) (let ((cast (cast-or-check-bound-type node (specifier-type 'real))))
(if cast (if cast
`(if (or (float-sign-bit-set-p x) `(if (or (< x 0)
;; The out of line definition returns 0 for all zeros
(< y 0)) (< y 0))
(sb-vm::op-not-type2-error x y '(,(type-specifier cast) . log)) (sb-vm::op-not-type2-error x y '(,(type-specifier cast) . log))
(truly-the $type (log (truly-the (float 0.0) x) (truly-the $type (log (truly-the (float 0.0) x)
@ -1107,15 +1106,13 @@
(defoptimizer (expt derive-type) ((x y)) (defoptimizer (expt derive-type) ((x y))
(two-arg-derive-type x y #'expt-derive-type-aux)) (two-arg-derive-type x y #'expt-derive-type-aux))
;;; Note we must assume that a type including 0.0 may also include
;;; -0.0 and thus the result may be complex -infinity + i*pi.
(defun log-derive-type-aux-1 (x) (defun log-derive-type-aux-1 (x)
(elfun-derive-type-simple x #'log (elfun-derive-type-simple x #'log
(if (integer-type-p x) 0 0d0) (if (integer-type-p x) 0 -0d0)
nil nil
;; (log 0) is an error ;; (log 0) is an error
;; and there's nothing between 0 and 1 forr integers. ;; and there's nothing between 0 and 1 for integers.
(and (integer-type-p x) 0f0) (and (integer-type-p x) -0f0)
nil)) nil))
(defun log-derive-type-aux-2 (x y same-arg) (defun log-derive-type-aux-2 (x y same-arg)

View file

@ -458,17 +458,16 @@ fractional bits."
(declare (double-float y)) (declare (double-float y))
(log y 2.0d0)) (log y 2.0d0))
(or double-float (complex double-float))) (or double-float (complex double-float)))
;; (assert-type (lambda (d) (assert-type (lambda (d)
;; (declare ((double-float 0d0) d)) (declare ((double-float 0d0) d))
;; (when (< d 1.0d0) (when (< d 1.0d0)
;; (log d))) (log d)))
;; (or null (double-float * 0.0d0))) (or null (double-float * 0.0d0)))
;; (assert-type (lambda (d) (assert-type (lambda (d)
;; (declare ((double-float 0d0) d)) (declare ((double-float 0d0) d))
;; (when (< d 1.0d0) (when (< d 1.0d0)
;; (log d 2.0d0))) (log d 2.0d0)))
;; (or null (double-float * 0.0d0))) (or null (double-float * 0.0d0))))
)
(with-test (:name (ftruncate :minus-zeros :one-arg)) (with-test (:name (ftruncate :minus-zeros :one-arg))
(flet ((f (x) (declare (notinline ftruncate)) (ftruncate x))) (flet ((f (x) (declare (notinline ftruncate)) (ftruncate x)))

View file

@ -26,9 +26,7 @@
(symbol (eql x y)) (symbol (eql x y))
(rational (eql x y)) (rational (eql x y))
(float (= x y)) (float (= x y))
(string (string= x y)) (string (string= x y)))))
;; KLUDGE
(complex t))))
(unless (eqal actual result) (unless (eqal actual result)
(cerror "Continue" (cerror "Continue"
"FLOAT CACHE LINE ~S vs COMPUTED ~S~%" "FLOAT CACHE LINE ~S vs COMPUTED ~S~%"