Clojure rounding to decimal places

2020-02-09 06:43发布

问题:

I have strings which represents the decimal values, ex: "0.010", "0.0100000" "00.01000"

I want to round them to specified format, ex: #.##

In Java we have:

public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
    return setScale(newScale, roundingMode.oldMode);
}

What is the best approach to achieve this in Clojure rather than using interop?

回答1:

You can use Clojure's format for this purpose. It should provide you a solution to your problem. Here are some examples and a reference:

user=> (format "%.2f" 0.010)
"0.01"
user=> (format "%.2f" 0.010000)
"0.01"
user=> (format "%.2f" 00.010000000)


user=> (doc format)
-------------------------
clojure.core/format
([fmt & args])
  Formats a string using java.lang.String.format, see java.util.Formatter for format
  string syntax


回答2:

This is a slightly modified version of an example at clojure-doc.org:

(defn round2
  "Round a double to the given precision (number of significant digits)"
  [precision d]
  (let [factor (Math/pow 10 precision)]
    (/ (Math/round (* d factor)) factor)))

@number23_cn's answer is the right one for many cases. However, a real rounding function with a precision argument may be useful if, for example, you want to display a sequence with every number rounded. Then you can simply map round2 over the sequence to format every number at once:

(map (partial round2 2) [0.001 10.123456 9.5556])

which returns

(0.0 10.12 9.56)

This is more useful for a longer sequence, of course.


Another option is to use cl-format, which is a Clojure implementation of Common Lisp's format. It's similar to Clojure's format (which is based on java.util.Formatter) but has a different syntax, and allows some fancier tricks.

(clojure.pprint/cl-format nil "~,2f" 23.456)
; => "23.46"

The ~{ ~} directive allows processing sequences, as in the first example above:

(clojure.pprint/cl-format nil "~{ ~,2f~}" [0.001 10.123456 9.5556])
; => " 0.00 10.12 9.56"

~{ ~} expects to see a sequence as an argument, and will eat elements of the sequence one by one using whatever directives appear between ~{ and ~}.

(The chapter on format from Peter Seibel's Practical Common Lisp is the best introduction to Common Lisp's format, and therefore to Clojure's cl-format, imo. The documentation on CL's format in the usual source, the Common Lisp Hyperspec, can be difficult to use sometimes. The section on CL's format in Common Lisp The Language is slightly better.)



回答3:

The accepted answer recommends format, yet format does not round (as pointed out in one of the comments). The other answer (by Mars) will not work for BigDecimals. To round bigdecs to a number of decimal places in Clojure the only solution I have found is to use Java interop:

(defn round [s]
  (fn [n]
    (assert (bigdec? n))
    (.setScale n s RoundingMode/HALF_EVEN)))

(def round4 (round 4)) 


回答4:

Using the function Double/ParseDouble after using format on the decimal number will return a decimal rounded to the desired number of decimals described by using format. Like so:

user=> (Double/parseDouble (format "%.2f" 0.009) ) 
0.01

If the rounded number is needed for further calculations, parsing a Double is important. However, if outputting a rounded number is all that is needed, then the use of format is appropriate.