Clojure四舍五入到小数位

时间:2012-05-25 09:13:54

标签: clojure decimal

我有表示十进制值的字符串, 例如:“0.010”,“0.0100000”“00.01000”

我想将它们舍入到指定的格式, 例如:#。##

在Java中我们有:

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

在Clojure中实现这一目标的最佳方法是什么,而不是使用互操作?

5 个答案:

答案 0 :(得分:30)

您可以将Clojure的format用于此目的。它应该为您提供问题的解决方案。以下是一些示例和参考:

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

答案 1 :(得分:20)

这是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的回答是正确的。但是,例如,如果要显示每个舍入数字的序列,则具有精度参数的实际舍入函数可能很有用。然后,您只需在序列上映射round2即可一次格式化每个数字:

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

返回

(0.0 10.12 9.56)

当然,这对更长的序列更有用。


另一个选择是使用cl-format,这是Common Lisp的format的Clojure实现。它类似于Clojure的format(基于java.util.Formatter)但具有不同的语法,并允许一些更高级的技巧。

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

~{ ~}指令允许处理序列,如上面的第一个示例所示:

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

~{ ~}期望看到一个序列作为参数,并且会使用~{~}之间出现的任何指令逐个吃掉序列的元素。

(来自Peter Seibel chapter on formatPractical Common Lisp是对Common Lisp format的最佳介绍,因此对Clojure的{{1} {{1}通常来源documentation on CL's format中的Common Lisp Hyperspec有时难以使用。section on CL's format中的Common Lisp The Language稍微好一点。)

答案 2 :(得分:1)

接受的答案建议format,但format没有舍入(正如其中一条评论所指出的那样)。另一个答案(由火星)不适用于BigDecimal。要将bigdec舍入到Clojure中的小数位数,我找到的唯一解决方案是使用Java互操作:

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

(def round4 (round 4)) 

答案 3 :(得分:0)

在小数点上使用Double/ParseDouble后使用函数format将返回一个小数舍入为使用format描述的所需小数位数。像这样:

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

如果需要舍入后的数字进行进一步的计算,则解析Double十分重要。但是,如果只需要输出舍入的数字,则使用format是适当的。

答案 4 :(得分:0)

在开始时将这些字符串转换为 BigDecimal 值很重要。任何数字文字(如 0.0001)或任何数值都可能被推断为双精度或浮点数,并且您可能会失去具有非常大比例的数字的精度。

第二件事是你可能不想明确格式化它,只是重新缩放到一些名义规模:

(defn round
  [n scale rm]
  (.setScale ^java.math.BigDecimal (bigdec n)
             (int scale)
             ^RoundingMode (if (instance? java.math.RoundingMode rm)
                             rm
                             (java.math.RoundingMode/valueOf
                              (str (if (ident? rm) (symbol rm) rm))))))

(round "12.345" 2 :UP)
12.35M

(round "12.345" 2 :DOWN)
12.34M

(round "12.345" 2 :HALF_UP)
12.35M

(round "12.3" 2 :HALF_EVEN)
12.30M

要在没有 M 字母的情况下打印它,只需使用 str.toPlainString

(str (round "0" 2 :HALF_EVEN))
"0.00"

(.toPlainString ^java.math.BigDecimal (round "1.2" 4 :UP))
"1.2000"

您还可以看到我如何在 Bankster library(用于玩弄货币和金钱)中抽象出类似的东西——寻找 apply 协议方法:

https://github.com/randomseed-io/bankster/blob/main/src/io/randomseed/bankster/scale.clj