From 4e58e53cc67db43bf6a1313a8f2f1038818c646d Mon Sep 17 00:00:00 2001
From: Raymond Toy <toy.raymond@gmail.com>
Date: Sun, 4 Feb 2018 08:18:26 -0800
Subject: [PATCH] Be more careful in computing the decode-float bounds

If 0 is the lower bound then the smallest exponent is not for 0, but
for the least positive float because of denormals.

Also handle exclusive bounds.
---
 src/compiler/float-tran.lisp | 42 ++++++++++++++++++++----------------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/src/compiler/float-tran.lisp b/src/compiler/float-tran.lisp
index 1ee030bd0..3df41f4b8 100644
--- a/src/compiler/float-tran.lisp
+++ b/src/compiler/float-tran.lisp
@@ -2031,29 +2031,33 @@
   (labels
       ((calc-exp (x)
 	 (when x
-	   (nth-value 1 (decode-float x))))
-       (min-exp ()
-	 ;; Use decode-float on the least positive float of the
-	 ;; appropriate type to find the min exponent.  If we don't
-	 ;; know the actual number format, use double, which has the
-	 ;; widest range (including double-double-float).
-	 (calc-exp (if (eq 'single-float (numeric-type-format arg))
-		       least-positive-single-float
-		       least-positive-double-float)))
-       (max-exp ()
+	   (bound-func #'(lambda (arg)
+			   (nth-value 1 (decode-float arg)))
+		       x)))
+       (min-exp (interval)
+	 ;; (decode-float 0d0) returns an exponent of -1022.  But
+	 ;; (decode-float least-positive-double-float returns -1073.
+	 ;; Hence, if the low value is less than this, we need to
+	 ;; return the exponent of the least positive number.
+	 (let ((least (if (eq 'single-float (numeric-type-format arg))
+			  least-positive-single-float
+			  least-positive-double-float)))
+	   (if (or (interval-contains-p 0 interval)
+		   (interval-contains-p least interval))
+	     (calc-exp least)
+	     (calc-exp (bound-value (interval-low interval))))))
+       (max-exp (interval)
 	 ;; Use decode-float on the most postive number of the
 	 ;; appropriate type to find the max exponent.  If we don't
 	 ;; know the actual number format, use double, which has the
 	 ;; widest range (including double-double-float).
-	 (calc-exp (if (eq 'single-float (numeric-type-format arg))
-		       most-positive-single-float
-		       most-positive-double-float))))
-    (let* ((interval (interval-func #'calc-exp
-				    (interval-abs (numeric-type->interval arg))))
-	   (lo (or (interval-low interval)
-		   (min-exp)))
-	   (hi (or (interval-high interval)
-		   (max-exp))))
+	 (or (calc-exp (bound-value (interval-high interval)))
+	     (calc-exp (if (eq 'single-float (numeric-type-format arg))
+			   most-positive-single-float
+			   most-positive-double-float)))))
+    (let* ((interval (interval-abs (numeric-type->interval arg)))
+	   (lo (min-exp interval))
+	   (hi (max-exp interval)))
       (specifier-type `(integer ,(or lo '*) ,(or hi '*))))))
 
 (defun decode-float-sign-derive-type-aux (arg)
-- 
GitLab