Scala Doubles, and Precision

前端 未结 12 1242
逝去的感伤
逝去的感伤 2020-12-12 21:19

Is there a function that can truncate or round a Double? At one point in my code I would like a number like: 1.23456789 to be rounded to 1.23

相关标签:
12条回答
  • 2020-12-12 21:51

    How about :

     val value = 1.4142135623730951
    
    //3 decimal places
    println((value * 1000).round / 1000.toDouble)
    
    //4 decimal places
    println((value * 10000).round / 10000.toDouble)
    
    0 讨论(0)
  • 2020-12-12 21:54

    Here's another solution without BigDecimals

    Truncate:

    (math floor 1.23456789 * 100) / 100
    

    Round:

    (math rint 1.23456789 * 100) / 100
    

    Or for any double n and precision p:

    def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
    

    Similar can be done for the rounding function, this time using currying:

    def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
    

    which is more reusable, e.g. when rounding money amounts the following could be used:

    def roundAt2(n: Double) = roundAt(2)(n)
    
    0 讨论(0)
  • 2020-12-12 21:56

    Since no-one mentioned the % operator yet, here comes. It only does truncation, and you cannot rely on the return value not to have floating point inaccuracies, but sometimes it's handy:

    scala> 1.23456789 - (1.23456789 % 0.01)
    res4: Double = 1.23
    
    0 讨论(0)
  • 2020-12-12 22:00

    It's actually very easy to handle using Scala f interpolator - https://docs.scala-lang.org/overviews/core/string-interpolation.html

    Suppose we want to round till 2 decimal places:

    scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
    sum: Double = 1.5697802197802198
    
    scala> println(f"$sum%1.2f")
    1.57
    
    0 讨论(0)
  • 2020-12-12 22:02

    You can do:Math.round(<double precision value> * 100.0) / 100.0 But Math.round is fastest but it breaks down badly in corner cases with either a very high number of decimal places (e.g. round(1000.0d, 17)) or large integer part (e.g. round(90080070060.1d, 9)).

    Use Bigdecimal it is bit inefficient as it converts the values to string but more relieval: BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue() use your preference of Rounding mode.

    If you are curious and want to know more detail why this happens you can read this:

    0 讨论(0)
  • 2020-12-12 22:04

    For those how are interested, here are some times for the suggested solutions...

    Rounding
    Java Formatter: Elapsed Time: 105
    Scala Formatter: Elapsed Time: 167
    BigDecimal Formatter: Elapsed Time: 27
    
    Truncation
    Scala custom Formatter: Elapsed Time: 3 
    

    Truncation is the fastest, followed by BigDecimal. Keep in mind these test were done running norma scala execution, not using any benchmarking tools.

    object TestFormatters {
    
      val r = scala.util.Random
    
      def textFormatter(x: Double) = new java.text.DecimalFormat("0.##").format(x)
    
      def scalaFormatter(x: Double) = "$pi%1.2f".format(x)
    
      def bigDecimalFormatter(x: Double) = BigDecimal(x).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
    
      def scalaCustom(x: Double) = {
        val roundBy = 2
        val w = math.pow(10, roundBy)
        (x * w).toLong.toDouble / w
      }
    
      def timed(f: => Unit) = {
        val start = System.currentTimeMillis()
        f
        val end = System.currentTimeMillis()
        println("Elapsed Time: " + (end - start))
      }
    
      def main(args: Array[String]): Unit = {
    
        print("Java Formatter: ")
        val iters = 10000
        timed {
          (0 until iters) foreach { _ =>
            textFormatter(r.nextDouble())
          }
        }
    
        print("Scala Formatter: ")
        timed {
          (0 until iters) foreach { _ =>
            scalaFormatter(r.nextDouble())
          }
        }
    
        print("BigDecimal Formatter: ")
        timed {
          (0 until iters) foreach { _ =>
            bigDecimalFormatter(r.nextDouble())
          }
        }
    
        print("Scala custom Formatter (truncation): ")
        timed {
          (0 until iters) foreach { _ =>
            scalaCustom(r.nextDouble())
          }
        }
      }
    
    }
    
    0 讨论(0)
提交回复
热议问题