From b8359d1e6db950b0a7bd4a255ed6d57a3eac08ef Mon Sep 17 00:00:00 2001 From: Stas Boukarev Date: Sat, 1 Sep 2012 01:38:13 +0400 Subject: [PATCH] Reading floats with large exponents no longer takes too much time. Reading 1.0s1000000000000000 will attempt to construct a very large bignum, which takes a considerable amount of time just to report in the end that it cannot be represented as a float. Truncate the exponent to manageable size before raising it. Fixes lp#309070. Thanks to Paul Khuong for the help. --- NEWS | 2 ++ src/code/reader.lisp | 22 +++++++++++++++++++++- tests/reader.pure.lisp | 5 +++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 647df01..47be00e 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ changes relative to sbcl-1.0.58: source annotation of DISASSEMBLE output. Defaults to T. * optimization: CL:SORT and CL:STABLE-SORT of lists are faster and use fewer comparisons, particularly on almost-sorted inputs. + * bug fix: Reading floats with large exponents no longer takes too much time + before reporting that the exponent is too large. * documentation: a section on random number generation has been added to the manual. (lp#656839) diff --git a/src/code/reader.lisp b/src/code/reader.lisp index 5d85c73..08a5286 100644 --- a/src/code/reader.lisp +++ b/src/code/reader.lisp @@ -1409,6 +1409,24 @@ extended :: syntax." (the index (* num base)))))))) (setq number (+ num (* number base-power))))))) +(defun truncate-exponent (exponent number divisor) + "Truncate exponent if it's too large for a float" + ;; Work with base-2 logarithms to avoid conversions to floats, + ;; and convert to base-10 conservatively at the end. + ;; Use the least positive float, because denormalized exponent + ;; can be larger than normalized. + (let* ((max-exponent (- (nth-value + 1 + (decode-float least-positive-long-float)))) + (number-magnitude (integer-length number)) + (divisor-magnitude (1- (integer-length divisor))) + (magnitude (- number-magnitude divisor-magnitude))) + (if (minusp exponent) + (max exponent (ceiling (- (+ max-exponent magnitude)) + (floor (log 10 2)))) + (min exponent (floor (- max-exponent magnitude) + (floor (log 10 2))))))) + (defun make-float (stream) ;; Assume that the contents of *read-buffer* are a legal float, with nothing ;; else after it. @@ -1469,6 +1487,7 @@ extended :: syntax." (#\F 'single-float) (#\D 'double-float) (#\L 'long-float))) + (exponent (truncate-exponent exponent number divisor)) (result (make-float-aux (* (expt 10 exponent) number) divisor float-format stream))) (return-from make-float @@ -1481,7 +1500,8 @@ extended :: syntax." (type-error (c) (error 'reader-impossible-number-error :error c :stream stream - :format-control "failed to build float")))) + :format-control "failed to build float from ~a" + :format-arguments (list (read-buffer-to-string)))))) (defun make-ratio (stream) ;; Assume *READ-BUFFER* contains a legal ratio. Build the number from diff --git a/tests/reader.pure.lisp b/tests/reader.pure.lisp index 7d29b18..04d9dda 100644 --- a/tests/reader.pure.lisp +++ b/tests/reader.pure.lisp @@ -283,3 +283,8 @@ (read-from-string "cl::'foo") (package-lock-violation () :violated!))))) + +(with-test (:name :bug-309070) + (with-timeout 10 + (assert (raises-error? (read-from-string "10e10000000000000000000") + sb-kernel:reader-impossible-number-error)))) -- 1.7.10.4