我有代表十进制值的字符串,例如:“0.010”、“0.0100000”、“00.01000”
我想将它们四舍五入到指定的格式,例如:#.##
在 Java 中,我们有:
public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
return setScale(newScale, roundingMode.oldMode);
}
在 Clojure 中实现这一目标而不是使用互操作的最佳方法是什么?
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
这是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
接受的答案建议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))
一开始就将这些字符串转换为 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
Double/ParseDouble在对十进制数使用后使用该函数format将返回一个四舍五入为使用描述的所需小数位数的小数format。像这样:
user=> (Double/parseDouble (format "%.2f" 0.009) )
0.01
如果进一步计算需要舍入数字,则解析 Double 很重要。但是,如果只需要输出一个四舍五入的数字,那么使用format是合适的。