Scala Doubles和Precision

ric*_*oni 108 double scala truncate rounding

是否有可以截断或舍入Double的函数?在我的代码中的某一点,我想要一个像:1.23456789舍入到的数字1.23

Tra*_*own 136

你可以使用scala.math.BigDecimal:

BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
Run Code Online (Sandbox Code Playgroud)

还有许多其他的舍入模式,遗憾的是目前还没有很好地记录(尽管他们的Java等价物是).

  • 我想有些人长期致电一个笨重的图书馆比简单的数学更容易理解.在这种情况下,我建议使用`"%.2f".format(x).toDouble`.只慢2倍,你只需要使用你已经知道的库. (25认同)
  • @JoséLeal - 嗯?`scala>"%.2f".format(0.714999999999).toDouble``res13:Double = 0.71`但是`scala>"%.2f".format(0.715).toDouble``res14:Double = 0.72`. (15认同)
  • @RexKerr,你不是在这种情况下四舍五入..简单地截断. (6认同)
  • @RexKerr我更喜欢你的string.format方式,但在locales sa mine(芬兰语)中,必须注意修复ROOT语言环境.例如"%.2f".formatLocal(java.util.Locale.ROOT,x).toDouble.看来,格式使用','因为语言环境,而toDouble无法接受它并抛出NumberFormatException.这当然是基于您的代码*运行*的位置,而不是它的开发位置. (5认同)
  • 我很可能会说.涉及网格或财务的任何事情都需要四舍五入和表现. (4认同)
  • @ely - 确实,如果您使用具有相当大运行时开销的 Scala 功能,Scala 会很慢。所以,在编写高性能代码时不要这样做。一些操作在 JVM 上很慢(比如随机索引并带有位掩码——JVM 会做边界检查),并且对于可以使 FFI 成为一个好主意的大型操作。但**情况并非如此**。问题是关于四舍五入**一个数字**。如果您想快速,则必须了解操作的性能特征。(例如避免`Option`,避免重复除法,避免FFI等。) (2认同)
  • @ely - 不,您不会_总是_避免使用 Scala 功能,而是在_知道代码需要快速_时避免使用 _slow 功能_。也就是说,不要对如何编写代码感到愚蠢;根据要求调整你写的内容。另外,您是否注意到我在单独的答案中提供了高性能版本?字符串答案只是获得正确行为的一种简单的低性能方式。如果有一个很好的内置浮点循环,那也很好,但并不是所有的东西都是内置的。最后,你能说出 C、C++、Haskell 和/或 FORTRAN 函数吗? (2认同)

Kai*_*ito 71

这是没有BigDecimals的另一种解决方案

截短:

(math floor 1.23456789 * 100) / 100
Run Code Online (Sandbox Code Playgroud)

回合:

(math rint 1.23456789 * 100) / 100
Run Code Online (Sandbox Code Playgroud)

或者对于任何双n和精度p:

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

对于舍入函数可以进行类似的操作,这次使用currying:

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

这是更可重复使用的,例如,当舍入金额时,可以使用以下内容:

def roundAt2(p: Int) = roundAt(2)(p)
Run Code Online (Sandbox Code Playgroud)

  • roundAt2应该是def roundAt2(n:Double)= roundAt(2)(n)不是吗? (8认同)

aka*_*ppi 32

由于没有人提到过%运营商,所以来了.它只会截断,你不能依赖返回值不会有浮点不准确,但有时它很方便:

scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
Run Code Online (Sandbox Code Playgroud)

  • 虽然这是我自己的答案,但不建议这样做:@ryryguy在另一个答案评论中提到的同样的不准确问题也会影响到这里.将string.format与Java ROOT语言环境一起使用(我将对此进行评论). (2认同)
  • 这里有趣的是:`26.257391515826225 - 0.057391515826223094 = 26.200000000000003` (2认同)

R S*_*Sun 9

使用 Scala 插值器实际上很容易处理f- https://docs.scala-lang.org/overviews/core/string-interpolation.html

假设我们想要四舍五入到小数点后两位:

scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198

scala> println(f"$sum%1.2f")
1.57
Run Code Online (Sandbox Code Playgroud)


blu*_*sky 8

怎么样 :

 val value = 1.4142135623730951

//3 decimal places
println((value * 1000).round / 1000.toDouble)

//4 decimal places
println((value * 10000).round / 10000.toDouble)
Run Code Online (Sandbox Code Playgroud)


Rex*_*err 7

编辑:解决了@ryryguy指出的问题.(谢谢!)

如果你想要它快,Kaito有正确的想法. math.pow但是很慢.对于任何标准用途,您最好使用递归函数:

def trunc(x: Double, n: Int) = {
  def p10(n: Int, pow: Long = 10): Long = if (n==0) pow else p10(n-1,pow*10)
  if (n < 0) {
    val m = p10(-n).toDouble
    math.round(x/m) * m
  }
  else {
    val m = p10(n).toDouble
    math.round(x*m) / m
  }
}
Run Code Online (Sandbox Code Playgroud)

如果你在Long(即18位数)的范围内,这大约快10倍,所以你可以在10 ^ 18到10 ^ -18之间的任何地方进行舍入.

  • 注意,乘以倒数不能可靠地工作,因为它可能不能可靠地表示为double:`scala> def r5(x:Double)= math.round(x*100000)*0.000001; r5(0.23515)`==>`res12:Double = 0.023514999999999998`.除以重要性:`math.round(x*100000)/ 100000.0` (3认同)

cev*_*ris 5

对于那些有兴趣的人,这里有一些建议的解决方案......

Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27

Truncation
Scala custom Formatter: Elapsed Time: 3 
Run Code Online (Sandbox Code Playgroud)

截断是最快的,其次是 BigDecimal。请记住,这些测试是在运行 norma scala 执行时完成的,没有使用任何基准测试工具。

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())
      }
    }
  }

}
Run Code Online (Sandbox Code Playgroud)