ANSI-TEST FORMAT.E.1 fails - FORMAT ~E is inconsistent with PRIN1

Bug #1854151 reported by Michał "phoe" Herda
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
New
Undecided
Unassigned

Bug Description

CLHS 22.1.3.1.3 states that FORMAT ~E is equivalent to PRIN1 for (abs x) outside of the range [10^-3,10^7), apart from the sign of the exponent, which is always printed. This is always not the case on SBCL, as there are rounding differences between PRIN1 and FORMAT ~E.

The test body from ANSI-TEST FORMAT.E.1 that triggers this behaviour:
https://gitlab.common-lisp.net/ansi-test/ansi-test/blob/f72a59e1/printer/format/format-e.lsp#L12

Extracted test body to run on SBCL without ANSI-TEST loaded:

(defmacro formatter-call-to-string (fn &body args)
  (let ((stream (gensym "S")))
    `(with-output-to-string (,stream)
       (assert (equal (funcall ,fn ,stream ,@args 'a) '(a))))))

(let ((*print-readably* nil)
      (fn (formatter "~e")))
  (loop
    for i = (random 4)
    for type = (elt #(short-float single-float double-float long-float)
                    i)
    for min-value = (elt (vector least-positive-short-float
                                 least-positive-single-float
                                 least-positive-double-float
                                 least-positive-long-float)
                         i)
    for max-value = (elt (vector most-positive-short-float
                                 most-positive-single-float
                                 most-positive-double-float
                                 most-positive-long-float)
                         i)
    for x = (expt (coerce 10 type)
                  (if (= (random 2) 0)
                      (- -3 (random (- -3 (log min-value 10))))
                      (+ 7 (random (- (log max-value 10) 7)))))
    for s1 = (let ((*read-default-float-format* type)) (format nil "~e" x))
    for s2 = (let* ((*read-default-float-format* type)
                    (s (prin1-to-string x))
                    (exp-pos (1+ (position #\e s :test #'char-equal))))
               (if (> x 1)
                   (concatenate 'string
                                (subseq s 0 exp-pos) "+" (subseq s exp-pos))
                   s))
    for s3 = (let ((*read-default-float-format* type))
               (formatter-call-to-string fn x))
    repeat 1000
    when (and (or (< x 1/1000)
                  (>= x 10000000))
              (or (not (string= s1 s2))
                  (not (string= s1 s3))))
      collect (list x s1 s2 s3)))

Some examples of the inconsistency returned by the above test:

(4.181094272874668d-74 "4.1810942728746675e-74" "4.181094272874668e-74"
                       "4.1810942728746675e-74")
(2.9215631313531054d-93 "2.921563131353106e-93" "2.9215631313531054e-93"
                        "2.921563131353106e-93")
(6.893129496037147d-158 "6.893129496037146e-158" "6.893129496037147e-158"
                        "6.893129496037146e-158")
(4.0289505140665034d-274 "4.028950514066504e-274" "4.0289505140665034e-274"
                         "4.028950514066504e-274")
(4.9146875e-20 "4.9146876e-20" "4.9146875e-20" "4.9146876e-20")

Revision history for this message
Michał "phoe" Herda (phoe-krk) wrote :

More rationale from CLHS 22.3.3.2:

If all of w, d, and e are omitted, then the effect is to print the value using ordinary free-format exponential-notation output; prin1 uses a similar format for any non-zero number whose magnitude is less than 10^-3 or greater than or equal to 10^7. The only difference is that the ~E directive always prints a plus or minus sign in front of the exponent, while prin1 omits the plus sign if the exponent is non-negative.

Revision history for this message
Michał "phoe" Herda (phoe-krk) wrote :

Attaching a patch ported from ECL's FORMAT.

Revision history for this message
Douglas Katzman (dougk) wrote :

this might have been fixed by https://sourceforge.net/p/sbcl/sbcl/ci/59195ae573fcfa2cf1244c55926263bb73aa9337
but I haven't checked. Could I ask you to re-test ?

Revision history for this message
Christophe Rhodes (csr21-cantab) wrote :

I didn't fix this in my recent ~E changes, sadly. We still scale the float, introducing inappropriate loss of precision compared with prin1.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.