Skip to content

Commit 04b5bfc

Browse files
jafingerhutstuarthalloway
authored andcommitted
CLJ-937: cl-format coerces ratio args for E,F,G directives to double or BigDecimal
Signed-off-by: Stuart Halloway <stu@thinkrelevance.com>
1 parent f083af8 commit 04b5bfc

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed

src/clj/clojure/pprint/cl_format.clj

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -543,13 +543,15 @@ Note this should only be used for the last one in the sequence"
543543
"Produce string parts for the mantissa (normalized 1-9) and exponent"
544544
[^Object f]
545545
(let [^String s (.toLowerCase (.toString f))
546-
exploc (.indexOf s (int \e))]
546+
exploc (.indexOf s (int \e))
547+
dotloc (.indexOf s (int \.))]
547548
(if (neg? exploc)
548-
(let [dotloc (.indexOf s (int \.))]
549-
(if (neg? dotloc)
550-
[s (str (dec (count s)))]
551-
[(str (subs s 0 dotloc) (subs s (inc dotloc))) (str (dec dotloc))]))
552-
[(str (subs s 0 1) (subs s 2 exploc)) (subs s (inc exploc))])))
549+
(if (neg? dotloc)
550+
[s (str (dec (count s)))]
551+
[(str (subs s 0 dotloc) (subs s (inc dotloc))) (str (dec dotloc))])
552+
(if (neg? dotloc)
553+
[(subs s 0 exploc) (subs s (inc exploc))]
554+
[(str (subs s 0 1) (subs s 2 exploc)) (subs s (inc exploc))]))))
553555

554556

555557
(defn- float-parts
@@ -650,13 +652,29 @@ string, or one character longer."
650652
(str "." m)
651653
(str (subs m 0 k) "." (subs m k))))
652654

655+
(defn- convert-ratio [x]
656+
(if (ratio? x)
657+
;; Usually convert to a double, only resorting to the slower
658+
;; bigdec conversion if the result does not fit within the range
659+
;; of a double.
660+
(let [d (double x)]
661+
(if (== d 0.0)
662+
(if (not= x 0)
663+
(bigdec x)
664+
d)
665+
(if (or (== d Double/POSITIVE_INFINITY) (== d Double/NEGATIVE_INFINITY))
666+
(bigdec x)
667+
d)))
668+
x))
669+
653670
;; the function to render ~F directives
654671
;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
655672
(defn- fixed-float [params navigator offsets]
656673
(let [w (:w params)
657674
d (:d params)
658675
[arg navigator] (next-arg navigator)
659676
[sign abs] (if (neg? arg) ["-" (- arg)] ["+" arg])
677+
abs (convert-ratio abs)
660678
[mantissa exp] (float-parts abs)
661679
scaled-exp (+ exp (:k params))
662680
add-sign (or (:at params) (neg? arg))
@@ -700,7 +718,8 @@ string, or one character longer."
700718
;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
701719
;; TODO: define ~E representation for Infinity
702720
(defn- exponential-float [params navigator offsets]
703-
(let [[arg navigator] (next-arg navigator)]
721+
(let [[arg navigator] (next-arg navigator)
722+
arg (convert-ratio arg)]
704723
(loop [[mantissa exp] (float-parts (if (neg? arg) (- arg) arg))]
705724
(let [w (:w params)
706725
d (:d params)
@@ -774,6 +793,7 @@ string, or one character longer."
774793
;; TODO: refactor so that float-parts isn't called twice
775794
(defn- general-float [params navigator offsets]
776795
(let [[arg _] (next-arg navigator)
796+
arg (convert-ratio arg)
777797
[mantissa exp] (float-parts (if (neg? arg) (- arg) arg))
778798
w (:w params)
779799
d (:d params)

test/clojure/test_clojure/pprint/test_cl_format.clj

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,8 +589,28 @@
589589
(format nil "~6,2F|~6,2,1,'*F|~6,2,,'?F|~6F|~,2F|~F"
590590
x x x x x x))
591591

592+
;; big-pos-ratio is a ratio value that is larger than
593+
;; Double/MAX_VALUE, and has a non-terminating decimal representation
594+
;; if you attempt to represent it exactly.
595+
(def big-pos-ratio (/ (* 4 (bigint (. BigDecimal valueOf Double/MAX_VALUE))) 3))
596+
(def big-neg-ratio (- big-pos-ratio))
597+
;; tiny-pos-ratio is a ratio between 0 and Double/MIN_VALUE.
598+
(def tiny-pos-ratio (/ 1 (bigint (apply str (cons "1" (repeat 340 "0"))))))
599+
(def tiny-neg-ratio (- tiny-pos-ratio))
600+
592601
(simple-tests cltl-F-tests
602+
(cl-format false "~10,3f" 4/5) " 0.800"
603+
(binding [*math-context* java.math.MathContext/DECIMAL128]
604+
(cl-format false "~10,3f" big-pos-ratio)) "239692417981642093333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000"
605+
(binding [*math-context* java.math.MathContext/DECIMAL128]
606+
(cl-format false "~10,3f" big-neg-ratio)) "-239692417981642093333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000"
607+
(binding [*math-context* java.math.MathContext/DECIMAL128]
608+
(cl-format false "~10,3f" tiny-pos-ratio)) " 0.000"
609+
(binding [*math-context* java.math.MathContext/DECIMAL128]
610+
(cl-format false "~10,3f" tiny-neg-ratio)) " -0.000"
593611
(foo 3.14159) " 3.14| 31.42| 3.14|3.1416|3.14|3.14159"
612+
(foo 314159/100000)
613+
" 3.14| 31.42| 3.14|3.1416|3.14|3.14159"
594614
(foo -3.14159) " -3.14|-31.42| -3.14|-3.142|-3.14|-3.14159"
595615
(foo 100.0) "100.00|******|100.00| 100.0|100.00|100.0"
596616
(foo 1234.0) "1234.00|******|??????|1234.0|1234.00|1234.0"
@@ -603,7 +623,18 @@
603623

604624
;; Clojure doesn't support float/double differences in representation
605625
(simple-tests cltl-E-tests
626+
(cl-format false "~10,3e" 4/5) " 8.000E-1"
627+
(binding [*math-context* java.math.MathContext/DECIMAL128]
628+
(cl-format false "~10,3e" big-pos-ratio)) "2.397E+308"
629+
(binding [*math-context* java.math.MathContext/DECIMAL128]
630+
(cl-format false "~10,3e" big-neg-ratio)) "-2.397E+308"
631+
(binding [*math-context* java.math.MathContext/DECIMAL128]
632+
(cl-format false "~10,3e" tiny-pos-ratio)) "1.000E-340"
633+
(binding [*math-context* java.math.MathContext/DECIMAL128]
634+
(cl-format false "~10,3e" tiny-neg-ratio)) "-1.000E-340"
606635
(foo-e 0.0314159) " 3.14E-2| 31.42$-03|+.003E+01| 3.14E-2" ; Added this one
636+
(foo-e 314159/10000000)
637+
" 3.14E-2| 31.42$-03|+.003E+01| 3.14E-2"
607638
(foo-e 3.14159) " 3.14E+0| 31.42$-01|+.003E+03| 3.14E+0"
608639
(foo-e -3.14159) " -3.14E+0|-31.42$-01|-.003E+03| -3.14E+0"
609640
(foo-e 1100.0) " 1.10E+3| 11.00$+02|+.001E+06| 1.10E+3"
@@ -641,7 +672,18 @@
641672

642673
;; Clojure doesn't support float/double differences in representation
643674
(simple-tests cltl-G-tests
675+
(cl-format false "~10,3g" 4/5) " 0.800 "
676+
(binding [*math-context* java.math.MathContext/DECIMAL128]
677+
(cl-format false "~10,3g" big-pos-ratio)) "2.397E+308"
678+
(binding [*math-context* java.math.MathContext/DECIMAL128]
679+
(cl-format false "~10,3g" big-neg-ratio)) "-2.397E+308"
680+
(binding [*math-context* java.math.MathContext/DECIMAL128]
681+
(cl-format false "~10,3g" tiny-pos-ratio)) "1.000E-340"
682+
(binding [*math-context* java.math.MathContext/DECIMAL128]
683+
(cl-format false "~10,3g" tiny-neg-ratio)) "-1.000E-340"
644684
(foo-g 0.0314159) " 3.14E-2|314.2$-04|0.314E-01| 3.14E-2"
685+
(foo-g 314159/10000000)
686+
" 3.14E-2|314.2$-04|0.314E-01| 3.14E-2"
645687
(foo-g 0.314159) " 0.31 |0.314 |0.314 | 0.31 "
646688
(foo-g 3.14159) " 3.1 | 3.14 | 3.14 | 3.1 "
647689
(foo-g 31.4159) " 31. | 31.4 | 31.4 | 31. "

0 commit comments

Comments
 (0)