Raymond Toy pushed to branch rtoy-print-using-ryu at cmucl / cmucl

Commits:

3 changed files:

Changes:

  • src/code/ryu-print.lisp
    ... ... @@ -358,6 +358,54 @@
    358 358
           (declare (dynamic-extent #'write-field))
    
    359 359
           (pad-overflow stream field-len w overflowchar padchar #'write-field))))
    
    360 360
     
    
    361
    +(defun emit-exp-no-fraction (stream mantissa shown-exp int-digits
    
    362
    +			     is-negative-p at-sign-p
    
    363
    +			     w e overflowchar padchar exponentchar)
    
    364
    +  "Emit \"[sign][INT-DIGITS].[marker][exp-sign][exp]\" with no
    
    365
    +   fractional digits, right-justified in width W.  MANTISSA is a
    
    366
    +   string of significant digits from d2s/d2exp, with or without an
    
    367
    +   internal dot; the integer digits are written from it (skipping the
    
    368
    +   dot if any), zero-padded to INT-DIGITS."
    
    369
    +  (declare (type simple-string mantissa)
    
    370
    +	   (fixnum shown-exp int-digits)
    
    371
    +	   (type (or null fixnum) w e))
    
    372
    +  (let* ((dotpos     (position #\. mantissa))
    
    373
    +	 (mant-len   (length mantissa))
    
    374
    +	 (sig-digits (mantissa-digit-count mantissa))
    
    375
    +	 (exp-sign   (if (minusp shown-exp) #\- #\+))
    
    376
    +	 (exp-abs    (abs shown-exp))
    
    377
    +	 (exp-digits (count-decimal-digits exp-abs))
    
    378
    +	 (exp-width  (max (or e 1) exp-digits))
    
    379
    +	 (exp-marker (or exponentchar #\d))
    
    380
    +	 (sign-len   (if (or is-negative-p at-sign-p) 1 0))
    
    381
    +	 ;; sign + int + dot + marker + exp-sign + exp-width
    
    382
    +	 (field-len  (+ sign-len int-digits 3 exp-width)))
    
    383
    +    (flet ((write-field ()
    
    384
    +	     (cond (is-negative-p (write-char #\- stream))
    
    385
    +		   (at-sign-p     (write-char #\+ stream)))
    
    386
    +	     ;; Write up to sig-digits from mantissa (skipping the dot),
    
    387
    +	     ;; then zero-pad if int-digits exceeds available.
    
    388
    +	     (let ((available (min int-digits sig-digits)))
    
    389
    +	       (cond ((null dotpos)
    
    390
    +		      (write-string mantissa stream :start 0 :end available))
    
    391
    +		     ((<= available dotpos)
    
    392
    +		      (write-string mantissa stream :start 0 :end available))
    
    393
    +		     (t
    
    394
    +		      (write-string mantissa stream :start 0 :end dotpos)
    
    395
    +		      (write-string mantissa stream
    
    396
    +				    :start (1+ dotpos)
    
    397
    +				    :end (min mant-len (1+ available))))))
    
    398
    +	     (loop repeat (- int-digits sig-digits)
    
    399
    +		   do (write-char #\0 stream))
    
    400
    +	     (write-char #\. stream)
    
    401
    +	     (write-char exp-marker stream)
    
    402
    +	     (write-char exp-sign stream)
    
    403
    +	     (loop repeat (- exp-width exp-digits)
    
    404
    +		   do (write-char #\0 stream))
    
    405
    +	     (princ exp-abs stream)))
    
    406
    +      (declare (dynamic-extent #'write-field))
    
    407
    +      (pad-overflow stream field-len w overflowchar padchar #'write-field))))
    
    408
    +
    
    361 409
     (defun format-e (value w d e k overflowchar padchar exponentchar at-sign-p)
    
    362 410
       (declare (type (or single-float double-float) value)
    
    363 411
                (fixnum k)
    
    ... ... @@ -371,8 +419,22 @@
    371 419
              (multiple-value-bind (mantissa exponent)
    
    372 420
                  (parsed-exp-form (d2exp abs-value
    
    373 421
     				     (d2exp-precision d k)))
    
    374
    -           (format-e-string stream mantissa exponent is-negative-p
    
    375
    -                            w e k overflowchar padchar exponentchar at-sign-p nil)))
    
    422
    +	   (cond
    
    423
    +	     ;; CLHS 22.3.3.2: with D specified, exactly d-(k-1)
    
    424
    +	     ;; fractional digits appear after the dot.  When that is 0
    
    425
    +	     ;; (k >= d+1, plusp k), emit "[int].e[exp]" directly with
    
    426
    +	     ;; no forced ".0" -- the d2exp result has been rounded to
    
    427
    +	     ;; exactly the digits we need to show.  The k=1, d=0 case
    
    428
    +	     ;; is excluded: cmucl has always emitted "D.0eN" there and
    
    429
    +	     ;; existing tests rely on it.
    
    430
    +	     ((and (>= k 2) (>= k (1+ d)))
    
    431
    +	      (emit-exp-no-fraction stream mantissa
    
    432
    +				    (- exponent (1- k))
    
    433
    +				    k is-negative-p at-sign-p
    
    434
    +				    w e overflowchar padchar exponentchar))
    
    435
    +	     (t
    
    436
    +	      (format-e-string stream mantissa exponent is-negative-p
    
    437
    +			       w e k overflowchar padchar exponentchar at-sign-p nil)))))
    
    376 438
             (t
    
    377 439
              (multiple-value-bind (mantissa exponent)
    
    378 440
                  (parsed-exp-form (float-to-string value))
    
    ... ... @@ -410,20 +472,10 @@
    410 472
                          ;; fall through to the old format-e-string path.
    
    411 473
                          (multiple-value-bind (one-digit shown-exp)
    
    412 474
                              (parsed-exp-form (d2exp abs-value 0))
    
    413
    -                       (let* ((exp-sign  (if (minusp shown-exp) #\- #\+))
    
    414
    -                              (exp-abs   (abs shown-exp))
    
    415
    -                              (exp-digits (count-decimal-digits exp-abs))
    
    416
    -                              (exp-width (max (or e 1) exp-digits))
    
    417
    -                              (exp-marker (or exponentchar #\d)))
    
    418
    -                         (cond (is-negative-p (write-char #\- stream))
    
    419
    -                               (at-sign-p     (write-char #\+ stream)))
    
    420
    -                         (write-string one-digit stream)
    
    421
    -                         (write-char #\. stream)
    
    422
    -                         (write-char exp-marker stream)
    
    423
    -                         (write-char exp-sign stream)
    
    424
    -                         (loop repeat (- exp-width exp-digits)
    
    425
    -                               do (write-char #\0 stream))
    
    426
    -                         (princ exp-abs stream))))
    
    475
    +                       (emit-exp-no-fraction stream one-digit shown-exp 1
    
    476
    +					     is-negative-p at-sign-p
    
    477
    +					     w e overflowchar padchar
    
    478
    +					     exponentchar)))
    
    427 479
                         (t
    
    428 480
                          ;; k != 1 and no width fits: fall back to the
    
    429 481
                          ;; shortest form (may still emit a forced ".0";
    

  • src/i18n/locale/cmucl.pot
    No preview for this file type
  • tests/ryu.lisp
    ... ... @@ -200,12 +200,15 @@
    200 200
     
    
    201 201
     (define-test format-e.overflow-no-overflowchar
    
    202 202
       (:tag :format-e)
    
    203
    -  ;; w too small AND no overflowchar -- emit the field at natural
    
    204
    -  ;; width, exceeding w.  No truncation.
    
    205
    -  (assert-equal "3.14d+100"
    
    203
    +  ;; w too small AND no overflowchar -- emit the no-fractional-digit
    
    204
    +  ;; form "d.e+exp" (overflowing W), per CLHS 22.3.3.2 "single zero
    
    205
    +  ;; digit ... if the width w permits" (here it does not).
    
    206
    +  (assert-equal "3.d+100"
    
    206 207
                     (lisp::format-e 3.14d100 3 nil nil 1 nil nil #\d nil))
    
    207
    -  ;; Free-format too long for w with no overflowchar.
    
    208
    -  (assert-equal "3.14159265358979d+0"
    
    208
    +  ;; Free-format too long for w with no overflowchar -- same rule:
    
    209
    +  ;; drop to no-fractional-digit form rather than emitting the full
    
    210
    +  ;; multi-digit shortest representation.
    
    211
    +  (assert-equal "3.d+0"
    
    209 212
                     (lisp::format-e 3.14159265358979d0 5 nil nil 1 nil nil #\d nil)))
    
    210 213
     
    
    211 214
     (define-test format-e.overflow-exponent-too-wide
    
    ... ... @@ -232,12 +235,14 @@
    232 235
     
    
    233 236
     (define-test format-e.overflow-shrink-exhausts
    
    234 237
       (:tag :format-e)
    
    235
    -  ;; w forces shrinking down to d_fit = 0, which still won't fit.
    
    236
    -  ;; Should produce overflow fill.
    
    238
    +  ;; w forces shrinking down to d_fit = nil (not even d=0 fits).
    
    239
    +  ;; With an overflow char, the field is filled.
    
    237 240
       (assert-equal "****"
    
    238 241
                     (lisp::format-e 3.14159265358979d0 4 nil nil 1 #\* nil #\d nil))
    
    239
    -  ;; Same but no overflowchar -- emit at natural shortest width.
    
    240
    -  (assert-equal "3.14159265358979d+0"
    
    242
    +  ;; Without overflowchar, emit the no-fractional-digit form
    
    243
    +  ;; "d.e+exp" (overflowing W) rather than the full multi-digit
    
    244
    +  ;; shortest representation.
    
    245
    +  (assert-equal "3.d+0"
    
    241 246
                     (lisp::format-e 3.14159265358979d0 4 nil nil 1 nil nil #\d nil)))
    
    242 247
     
    
    243 248
     (define-test format-e.overflow-with-padchar
    
    ... ... @@ -306,6 +311,24 @@
    306 311
       (assert-equal "-1.e+0"
    
    307 312
                     (lisp::format-e -1.0f0 6 nil nil 1 nil nil #\e nil)))
    
    308 313
     
    
    314
    +(define-test format-e.d-given-k-consumes-fraction
    
    315
    +  (:tag :format-e)
    
    316
    +  ;; ANSI FORMAT.E.19: with D specified, the number of fractional
    
    317
    +  ;; digits is exactly d-(k-1).  When k >= d+1 (and k >= 2) that count
    
    318
    +  ;; is 0, and the output is "[int].e[exp]" with no forced ".0".
    
    319
    +  ;; d=2, k=3, value 0.05 -> "5.00E-2" rounded by d2exp -> "500.e-4"
    
    320
    +  ;; after the k-shift consumes the fractional digits.
    
    321
    +  (assert-equal "500.e-4"
    
    322
    +                (lisp::format-e 0.05f0 nil 2 nil 3 nil nil #\e nil))
    
    323
    +  (assert-equal "500.e-4"
    
    324
    +                (lisp::format-e 0.05d0 nil 2 nil 3 nil nil #\e nil))
    
    325
    +  ;; Negated: leading "-" sign included.
    
    326
    +  (assert-equal "-500.e-4"
    
    327
    +                (lisp::format-e -0.05d0 nil 2 nil 3 nil nil #\e nil))
    
    328
    +  ;; At-sign: explicit "+".
    
    329
    +  (assert-equal "+500.e-4"
    
    330
    +                (lisp::format-e 0.05d0 nil 2 nil 3 nil nil #\e t)))
    
    331
    +
    
    309 332
     ;;; ~F tests
    
    310 333
     (define-test format-f.basic
    
    311 334
       (:tag :format-f)