我有表示十进制值的字符串, 例如:“0.010”,“0.0100000”“00.01000”
我想将它们舍入到指定的格式, 例如:#。##
在Java中我们有:
public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
return setScale(newScale, roundingMode.oldMode);
}
在Clojure中实现这一目标的最佳方法是什么,而不是使用互操作?
答案 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 format
的Practical 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