Raymond Toy pushed to branch rtoy-print-using-ryu at cmucl / cmucl Commits: dc28a0ad by Raymond Toy at 2026-05-26T21:11:56-07:00 Fix Ryu format-g to pass user's d to format-e Previously, format-g called format-e with `effective-d` instead of `d`. But CLHS 22.3.3.3 specifies that when ~E is called, the user's original d is used. One example Before: (format nil "~g" 1.5d-10) => "1.50d-10" After: (format nil "~g" 1.5d-10) => "1.5d-10" The latter is correct. Adjusted the existing format-g tests and added a regression test for 1.5d-10. - - - - - 2 changed files: - src/code/ryu-print.lisp - tests/ryu.lisp Changes: ===================================== src/code/ryu-print.lisp ===================================== @@ -555,6 +555,10 @@ (format-f value ww dd 0 overflowchar padchar at-sign-p) (make-string ee :initial-element #\space))) (t - ;; ~E form - (format-e value w effective-d e k overflowchar padchar exponentchar at-sign-p)))))) + ;; ~E form. CLHS 22.3.3.3 specifies the resulting call as + ;; "~w,d,e,k,...,E" -- using the user's original D, not the + ;; EFFECTIVE-D computed for the ~F-vs-~E decision. When D is + ;; NIL this lets format-e produce its free-format (shortest) + ;; output rather than fixed-precision. + (format-e value w d e k overflowchar padchar exponentchar at-sign-p)))))) ===================================== tests/ryu.lisp ===================================== @@ -518,11 +518,12 @@ (define-test format-g.large-uses-e (:tag :format-g) ;; Values with n > 7 should fall into ~E form (no trailing spaces). - ;; 1d10 has n=11, q=1, effective-d = max(1, 7) = 7, dd = 7-11 = -4. - ;; ~E with effective-d = 7: 1.0000000d+10. - (assert-equal "1.0000000d+10" + ;; Per CLHS 22.3.3.3, when D is unspecified ~G's ~E call also has + ;; D unspecified, so ~E uses free-format (shortest) output rather + ;; than fixed precision. + (assert-equal "1.0d+10" (lisp::format-g 1d10 nil nil nil 1 nil nil #\d nil)) - (assert-equal "1.0000000d+100" + (assert-equal "1.0d+100" (lisp::format-g 1d100 nil nil nil 1 nil nil #\d nil))) (define-test format-g.small-uses-e @@ -563,8 +564,9 @@ (:tag :format-g) ;; e parameter controls exponent width in the ~E branch and ee in ;; the ~F branch (ee = e + 2). - ;; 1d10 with e=3: ~E with 3-digit exponent, "1.0000000d+010". - (assert-equal "1.0000000d+010" + ;; 1d10 with e=3: ~E with 3-digit exponent. D is unspecified so + ;; ~E uses free-format, giving "1.0d+010". + (assert-equal "1.0d+010" (lisp::format-g 1d10 nil nil 3 1 nil nil #\d nil)) ;; 3.14 with e=3: ~F branch, ee = 5 trailing spaces. (assert-equal "3.14 " @@ -577,7 +579,7 @@ (lisp::format-g 3.14d0 nil nil nil 1 nil nil #\d t)) (assert-equal "-3.14 " (lisp::format-g -3.14d0 nil nil nil 1 nil nil #\d t)) - (assert-equal "+1.0000000d+10" + (assert-equal "+1.0d+10" (lisp::format-g 1d10 nil nil nil 1 nil nil #\d t))) (define-test format-g.boundary-n-7 @@ -587,10 +589,26 @@ ;; dd = 0 is in [0, d] so ~F path with d=0. Output: "1000000." + 4 sp. (assert-equal "1000000. " (lisp::format-g 1d6 nil nil nil 1 nil nil #\d nil)) - ;; 1d7 has n=8, dd = 7-8 = -1, falls into ~E. - (assert-equal "1.0000000d+7" + ;; 1d7 has n=8, dd = 7-8 = -1, falls into ~E. D is unspecified so + ;; ~E uses free-format. + (assert-equal "1.0d+7" (lisp::format-g 1d7 nil nil nil 1 nil nil #\d nil))) +(define-test format-g.d-nil-routes-to-e-with-d-nil + (:tag :format-g) + ;; CLHS 22.3.3.3 specifies that when ~G falls through to the ~E + ;; branch, the ~E directive is called with the user's original D + ;; (per "~w,d,e,k,...,E"), NOT the EFFECTIVE-D that ~G computed for + ;; the ~F-vs-~E decision. This makes a visible difference when D + ;; was originally NIL: ~E then produces its free-format (shortest) + ;; output rather than the longer fixed-precision form. + ;; 1.5d-10 has n=-9, q=2, effective-d=2, dd=11 -> ~E with D=nil. + (assert-equal "1.5d-10" + (lisp::format-g 1.5d-10 nil nil nil 1 nil nil #\d nil)) + ;; If we passed effective-d=2 to ~E we'd get "1.50d-10" instead. + (assert-false (equal "1.50d-10" + (lisp::format-g 1.5d-10 nil nil nil 1 nil nil #\d nil)))) + (define-test format-g.padchar (:tag :format-g) ;; padchar passed through to whichever sub-formatter fires. View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/dc28a0ad6ab31c787d37ca4e... -- View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/commit/dc28a0ad6ab31c787d37ca4e... You're receiving this email because of your account on gitlab.common-lisp.net. Manage all notifications: https://gitlab.common-lisp.net/-/profile/notifications | Help: https://gitlab.common-lisp.net/help
participants (1)
-
Raymond Toy (@rtoy)