Raymond Toy pushed to branch master at cmucl / cmucl

Commits:

3 changed files:

Changes:

  • src/code/reader.lisp
    ... ... @@ -1785,7 +1785,7 @@ the end of the stream."
    1785 1785
         ;; Is there an exponent letter?
    
    1786 1786
         (cond ((eofp char)
    
    1787 1787
                ;; If not, we've read the whole number.
    
    1788
    -           (let ((num (make-float-aux number divisor
    
    1788
    +           (let ((num (make-float-aux 0 number divisor
    
    1789 1789
                                           *read-default-float-format*
    
    1790 1790
     				      stream)))
    
    1791 1791
                  (return-from make-float (if negative-fraction (- num) num))))
    
    ... ... @@ -1815,7 +1815,7 @@ the end of the stream."
    1815 1815
     				  #+double-double
    
    1816 1816
     				  (#\W 'kernel:double-double-float)))
    
    1817 1817
                       num)
    
    1818
    -	     (setq num (make-float-aux (* (expt 10 exponent) number) divisor
    
    1818
    +	     (setq num (make-float-aux exponent number divisor
    
    1819 1819
     				       float-format stream))
    
    1820 1820
     
    
    1821 1821
     	     (return-from make-float (if negative-fraction
    
    ... ... @@ -1823,10 +1823,55 @@ the end of the stream."
    1823 1823
                                                  num))))
    
    1824 1824
     	  (t (error _"Internal error in floating point reader.")))))
    
    1825 1825
     
    
    1826
    -(defun make-float-aux (number divisor float-format stream)
    
    1826
    +(defun make-float-aux (exponent number divisor float-format stream)
    
    1827
    +  ;; Computes x = number*10^exponent/divisor.
    
    1828
    +  ;;
    
    1829
    +  ;; First check to see if x can possibly fit into a float of the
    
    1830
    +  ;; given format.  So compute log2(x) to get an approximate value of
    
    1831
    +  ;; the base 2 exponent of x.  If it's too large or too small, we can
    
    1832
    +  ;; throw an error immediately.  We don't need to be super accurate
    
    1833
    +  ;; with the limits.  The rest of the code will handle it correctly,
    
    1834
    +  ;; even if we're too small or too large.
    
    1835
    +  (unless (zerop number)
    
    1836
    +    (flet ((fast-log2 (n)
    
    1837
    +             ;;  For an integer, the integer-length is close enough to
    
    1838
    +             ;;  the log2 of the number.
    
    1839
    +             (integer-length n)))
    
    1840
    +      ;; log2(x) = log(number*10^exponent/divisor)
    
    1841
    +      ;;         = exponent*log2(10) + log2(number)-log2(divisor)
    
    1842
    +      (let ((log2-num (+ (* exponent #.(kernel::log2 10d0))
    
    1843
    +                         (fast-log2 number)
    
    1844
    +                         (- (fast-log2 divisor)))))
    
    1845
    +        (multiple-value-bind (log2-low log2-high)
    
    1846
    +            (ecase float-format
    
    1847
    +              ((short-float single-float)
    
    1848
    +               ;; Single-float exponents range is -149 to 127, but we
    
    1849
    +               ;; don't need to be super-accurate since we're
    
    1850
    +               ;; multiplying the values by 2.
    
    1851
    +               (values (* 2 (- vm:single-float-normal-exponent-min
    
    1852
    +                               vm:single-float-bias
    
    1853
    +                               vm:single-float-digits))
    
    1854
    +                       (* 2 (- vm:single-float-normal-exponent-max
    
    1855
    +                               vm:single-float-bias))))
    
    1856
    +              ((double-float long-float
    
    1857
    +                             #+double-double kernel:double-double-float)
    
    1858
    +               (values (* 2 (- vm:double-float-normal-exponent-min
    
    1859
    +                               vm:double-float-bias
    
    1860
    +                               vm:double-float-digits))
    
    1861
    +                       (* 2 (- vm:double-float-normal-exponent-max
    
    1862
    +                               vm:double-float-bias)))))
    
    1863
    +          ;; Double-float exponent range is -1074 to -1023
    
    1864
    +          (unless (< log2-low log2-num log2-high)
    
    1865
    +            ;; The number is definitely too large or too small to fit.
    
    1866
    +            ;; Signal an error.
    
    1867
    +            (%reader-error stream _"Number not representable as a ~S: ~S"
    
    1868
    +			   float-format (read-buffer-to-string)))))))
    
    1869
    +
    
    1870
    +  ;; Otherwise the number might fit, so we carefully compute the result
    
    1827 1871
       (handler-case
    
    1828 1872
           (with-float-traps-masked (:underflow)
    
    1829
    -	(let* ((ratio (/ number divisor))
    
    1873
    +	(let* ((ratio (/ (* (expt 10 exponent) number)
    
    1874
    +                         divisor))
    
    1830 1875
     	       (result (coerce ratio float-format)))
    
    1831 1876
     	  (when (and (zerop result) (not (zerop number)))
    
    1832 1877
     	    ;; The number we've read is so small that it gets
    
    ... ... @@ -1850,7 +1895,7 @@ the end of the stream."
    1850 1895
     	  result))
    
    1851 1896
         (error ()
    
    1852 1897
     	   (%reader-error stream _"Number not representable as a ~S: ~S"
    
    1853
    -			  float-format (/ number divisor)))))
    
    1898
    +			  float-format (read-buffer-to-string)))))
    
    1854 1899
     
    
    1855 1900
     
    
    1856 1901
     (defun make-ratio (stream)
    

  • src/i18n/locale/cmucl.pot
    ... ... @@ -8728,11 +8728,11 @@ msgid "Internal error in floating point reader."
    8728 8728
     msgstr ""
    
    8729 8729
     
    
    8730 8730
     #: src/code/reader.lisp
    
    8731
    -msgid "Underflow"
    
    8731
    +msgid "Number not representable as a ~S: ~S"
    
    8732 8732
     msgstr ""
    
    8733 8733
     
    
    8734 8734
     #: src/code/reader.lisp
    
    8735
    -msgid "Number not representable as a ~S: ~S"
    
    8735
    +msgid "Underflow"
    
    8736 8736
     msgstr ""
    
    8737 8737
     
    
    8738 8738
     #: src/code/reader.lisp
    

  • tests/float.lisp
    ... ... @@ -182,3 +182,33 @@
    182 182
           (assert-equal least-positive-double-float
    
    183 183
                         (kernel::float-ratio-float (* 988/100 expo) 'double-float)))))
    
    184 184
         
    
    185
    +(define-test reader-error.small-single-floats
    
    186
    +    (:tag :issues)
    
    187
    +  ;; Test a number less than half of least-positive-single-float,
    
    188
    +  ;; something a bit smaller, hen then something really small that
    
    189
    +  ;; used to appear to hang cmucl because it was trying to compute the
    
    190
    +  ;; a rational with a huge number of digits.
    
    191
    +  (dolist (num '("1e-46" "1e-80" "1e-999999999"))
    
    192
    +    (assert-error 'reader-error (read-from-string num)
    
    193
    +                  num)))
    
    194
    +
    
    195
    +(define-test reader-error.small-double-floats
    
    196
    +    (:tag :issues)
    
    197
    +  ;; Like reader-error.small-single-floats but for doubles
    
    198
    +  (dolist (num '("1d-324" "1d-600" "1d-999999999"))
    
    199
    +    (assert-error 'reader-error (read-from-string num)
    
    200
    +                  num)))
    
    201
    +
    
    202
    +(define-test reader-error.big-single-floats
    
    203
    +    (:tag :issues)
    
    204
    +  ;; Signal error for a number just a bit larger than
    
    205
    +  ;; most-positive-single-float.  And a really big single-float.
    
    206
    +  (assert-error 'reader-error (read-from-string "3.5e38"))
    
    207
    +  (assert-error 'reader-error (read-from-string "1e999999999")))
    
    208
    +
    
    209
    +(define-test reader-error.big-double-floats
    
    210
    +    (:tag :issues)
    
    211
    +  ;; Signal error for a number just a bit larger than
    
    212
    +  ;; most-positive-double-float.  And a really big single-float.
    
    213
    +  (assert-error 'reader-error (read-from-string "1.8d308"))
    
    214
    +  (assert-error 'reader-error (read-from-string "1d999999999")))