23

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

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

在 Java 中,我们有:

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

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

4

5 回答 5

39

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

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
于 2012-05-25T09:45:10.737 回答
22

这是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 的 Clojure 实现format。它类似于 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 的Practical Common Lisp的章节format是对 Common Lisp 的最佳介绍,因此也是对 Clojure 和imo 的最佳介绍。通常来源Common Lisp Hyperspec中关于 CL 的文档有时可能难以使用。关于 CL 的部分Common Lisp 中语言稍微好一点。)formatcl-formatformatformat

于 2014-08-02T19:23:16.303 回答
2

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

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

(def round4 (round 4)) 
于 2017-09-09T21:45:56.973 回答
1

一开始就将这些字符串转换为 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要在没有字母的情况下打印它,只需使用stror .toPlainString

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

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

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

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

于 2021-07-17T14:08:58.550 回答
1

Double/ParseDouble在对十进制数使用后使用该函数format将返回一个四舍五入为使用描述的所需小数位数的小数format。像这样:

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

如果进一步计算需要舍入数字,则解析 Double 很重要。但是,如果只需要输出一个四舍五入的数字,那么使用format是合适的。

于 2019-04-10T18:23:14.980 回答