【问题标题】:Clojure rounding to decimal placesClojure 舍入到小数位
【发布时间】:2012-05-25 09:13:53
【问题描述】:

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

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

在 Java 中我们有:

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

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

【问题讨论】:

    标签: clojure decimal


    【解决方案1】:

    您可以为此目的使用 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
    

    【讨论】:

    • 应该使用 cl-format 代替 format - format 只是 java.util.Formatter 的一个薄包装,因此它不处理 Clojure 的 BigInt。从 Clojure 1.3 开始,如果您的数字长时间太大,它将在 BigInt 中溢出,这是 Clojure 实现,而不是 BigInteger,它是 Java 实现。正如您可能猜到的那样,java.util.Formatter 不处理 Clojure 实现并引发异常。
    • 不圆,只是铺地板。
    • Java 8 docs 似乎表明它确实是圆的
    【解决方案2】:

    这是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 的Practical Common Lisp 中的chapter on format 是对Common Lisp 的format 的最佳介绍,因此也是对Clojure 的cl-format 的最佳介绍,imo。通常来源中的documentation on CL's formatCommon Lisp Hyperspec,可以有时很难使用。Common Lisp The Language 中的section on CL's format 稍微好一点。)

    【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

        在开始时将这些字符串转换为 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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多