Raymond Toy pushed to branch rtoy-print-using-ryu at cmucl / cmucl Commits: fbb33ee9 by Raymond Toy at 2026-05-25T07:00:06-07:00 Handle k=nil in format-fixed-ryu It was done in format-fixed-aux, but it makes more sense to do this in format-fixed-ryu. If k=nil, set k = 0. - - - - - cf9d41e9 by Raymond Toy at 2026-05-25T07:10:10-07:00 Fix ansi-test format.f.13 failure The output should be ".50" instead of "0.50" that we were producing. Tests updated. The update includes things that we haven't fixed yet, so skip CI. [skip-ci] - - - - - 3 changed files: - src/code/format.lisp - src/code/ryu-print.lisp - tests/ryu.lisp Changes: ===================================== src/code/format.lisp ===================================== @@ -1585,7 +1585,7 @@ ((and lisp::*use-ryu-printer* (or (null k) (zerop k)) (typep number '(or single-float double-float))) - (format-fixed-ryu stream number w d (or k 0) ovf pad atsign)) + (format-fixed-ryu stream number w d k ovf pad atsign)) (t (format-fixed-aux-bd stream number w d k ovf pad atsign)))) @@ -1599,7 +1599,7 @@ (prin1 number stream) nil) (t - (write-string (lisp::format-f number w d k ovf pad atsign) stream))) + (write-string (lisp::format-f number w d (or k 0) ovf pad atsign) stream))) nil) (defun format-fixed-aux-bd (stream number w d k ovf pad atsign) ===================================== src/code/ryu-print.lisp ===================================== @@ -412,11 +412,32 @@ (raw-len (length raw-string)) (sign-len (if (or is-negative-p at-sign-p) 1 0)) (need-dot (zerop d)) - (field-len (+ sign-len raw-len (if need-dot 1 0)))) + (full-field-len (+ sign-len raw-len (if need-dot 1 0))) + ;; CLHS 22.3.3.1: the leading zero before the decimal point + ;; is optional when the magnitude is nonzero and less than 1. + ;; d2fixed always emits it (e.g. "0.50" for 0.5), so detect + ;; that case here and drop the zero if the field would not + ;; otherwise fit in W. For exact zero the leading digit is + ;; required, so PLUSP ABS-VALUE gates the dropping. + (lpoint-droppable + (and (plusp abs-value) + (>= raw-len 2) + (char= (char raw-string 0) #\0) + (char= (char raw-string 1) #\.))) + (drop-leading-zero-p + (and lpoint-droppable + w + (> full-field-len w))) + (field-len (if drop-leading-zero-p + (1- full-field-len) + full-field-len))) (flet ((write-field () (cond (is-negative-p (write-char #\- stream)) (at-sign-p (write-char #\+ stream))) - (write-string raw-string stream) + (cond (drop-leading-zero-p + (write-string raw-string stream :start 1)) + (t + (write-string raw-string stream))) (when need-dot (write-char #\. stream)))) (declare (dynamic-extent #'write-field)) (pad-overflow stream field-len w overflowchar padchar #'write-field))))) @@ -482,17 +503,21 @@ (d-fit (and w (- w sign-len int-len 1)))) (cond ((or (null w) (>= d-fit shortest-d)) + ;; No width or the shortest form fits within a field width + ;; of W. (emit-shortest stream mantissa exponent is-negative-p w overflowchar padchar at-sign-p)) - ((minusp d-fit) - (cond (overflowchar - (loop repeat w do (write-char overflowchar stream))) - (t - (emit-shortest stream mantissa exponent is-negative-p w - overflowchar padchar at-sign-p)))) - (t - (format-f-fixed stream value w d-fit - overflowchar padchar at-sign-p))))))) + (overflowchar + ;; Shortest form does not fit and OVERFLOWCHAR is set; fill + ;; the field with overflow characters. + (loop repeat w + do (write-char overflowchar stream))) + (t + ;; Shortest form does not fit and no OVERFLOWCHAR; emit the + ;; full shortest form, letting the field expand. CLHS + ;; 22.3.3.1 requires this. + (emit-shortest stream mantissa exponent is-negative-p w + overflowchar padchar at-sign-p))))))) (defun format-f (value w d k overflowchar padchar at-sign-p) (declare (type (or single-float double-float) value) ===================================== tests/ryu.lisp ===================================== @@ -732,6 +732,68 @@ (assert-equal " 3.14" (lisp::format-f 3.14f0 10 nil 0 nil nil nil))) +(define-test format-f.single-narrow-width-no-overflow-char + (:tag :format-f :single-float) + ;; CLHS 22.3.3.1: when the shortest form does not fit in the + ;; specified width and no overflow character is supplied, the field + ;; expands rather than truncating digits. Round-trip and the + ;; "at least one digit after the decimal point" rule must both hold. + (assert-equal "1.0" + (lisp::format-f 1.0f0 2 nil 0 nil nil nil)) + (assert-equal "1.0" + (lisp::format-f 1.0f0 1 nil 0 nil nil nil)) + ;; Same for double-floats. + (assert-equal "1.0" + (lisp::format-f 1.0d0 2 nil 0 nil nil nil))) + +(define-test format-f.single-narrow-width-with-overflow-char + (:tag :format-f :single-float) + ;; With overflowchar supplied, the field is filled with the overflow + ;; character when the shortest form does not fit. + (assert-equal "**" + (lisp::format-f 1.0f0 2 nil 0 #\* nil nil)) + (assert-equal "***" + (lisp::format-f 1234.5f0 3 nil 0 #\* nil nil))) + +(define-test format-f.optional-leading-zero + (:tag :format-f :single-float :double-float) + ;; CLHS 22.3.3.1: "a zero is printed before the decimal point if + ;; there is room". When the field is too narrow to fit the leading + ;; zero, it is dropped, and the output starts with the decimal point. + ;; ANSI tests FORMAT.F.13-16. + ;; + ;; ~3,2F of 0.5 -> ".50" + (assert-equal ".50" (lisp::format-f 0.5f0 3 2 0 nil nil nil)) + (assert-equal ".50" (lisp::format-f 0.5d0 3 2 0 nil nil nil)) + ;; ~2,1F of 0.5 -> ".5" + (assert-equal ".5" (lisp::format-f 0.5f0 2 1 0 nil nil nil)) + (assert-equal ".5" (lisp::format-f 0.5d0 2 1 0 nil nil nil)) + ;; ~4,2@F of 0.5 -> "+.50" + (assert-equal "+.50" (lisp::format-f 0.5f0 4 2 0 nil nil t)) + (assert-equal "+.50" (lisp::format-f 0.5d0 4 2 0 nil nil t)) + ;; ~2,2F of 0.5 -> ".50" (field expands beyond w=2 since no overflowchar) + (assert-equal ".50" (lisp::format-f 0.5f0 2 2 0 nil nil nil)) + (assert-equal ".50" (lisp::format-f 0.5d0 2 2 0 nil nil nil)) + ;; Negative values: ~3,2F of -0.5 -> "-.50" (field expands past w=3). + (assert-equal "-.50" (lisp::format-f -0.5d0 3 2 0 nil nil nil)) + ;; -0.5 in width 4: drops the zero, fits exactly. + (assert-equal "-.50" (lisp::format-f -0.5d0 4 2 0 nil nil nil))) + +(define-test format-f.leading-zero-kept-when-room + (:tag :format-f :single-float :double-float) + ;; When there is room for the leading zero, it must be printed. + (assert-equal " 0.50" (lisp::format-f 0.5d0 5 2 0 nil nil nil)) + (assert-equal "0.50" (lisp::format-f 0.5d0 4 2 0 nil nil nil)) + (assert-equal "0.50" (lisp::format-f 0.5d0 nil 2 0 nil nil nil))) + +(define-test format-f.leading-zero-required-for-exact-zero + (:tag :format-f :single-float :double-float) + ;; CLHS 22.3.3.1: "If the magnitude is exactly zero, a single zero + ;; digit is printed before the decimal point." Don't drop it even + ;; if the field would overflow. + (assert-equal "0.00" (lisp::format-f 0.0d0 3 2 0 nil nil nil)) + (assert-equal "0.00" (lisp::format-f 0.0f0 3 2 0 nil nil nil))) + (define-test format-f.single-negative-zero (:tag :format-f :single-float) (assert-equal "-0.0" View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/688dc657b478b490021177d... -- View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/688dc657b478b490021177d... 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)