斯卡拉双打和精度

有一个函数可以截断或舍入Double吗? 在我的代码中的一点,我想要一个数字,如: 1.23456789四舍五入到1.23

你可以使用scala.math.BigDecimal

 BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble 

还有一些其他的舍入模式 ,不幸的是目前还没有很好的logging(虽然他们的Java等价物 )。

这是没有BigDecimals的另一个解决scheme

截短:

 (math floor 1.23456789 * 100) / 100 

回合:

 (math rint 1.23456789 * 100) / 100 

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

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

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

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

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

 def roundAt2(p: Int) = roundAt(2)(p) 

由于没有人提到%运算符,所以来了。 它只是截断,你不能依靠返回值没有浮点不准确,但有时它是方便的:

 scala> 1.23456789 - (1.23456789 % 0.01) res4: Double = 1.23 

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

如果你想要它快,Kaito有正确的想法。 math.pow是缓慢的,虽然。 对于任何标准的使用,最好使用recursion函数:

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

如果你在Long (即18位)的范围内,这个速度大约快10倍,所以你可以在10 ^ 18到10 ^ -18之间的任何地方进行轮回。

怎么样 :

  val value = 1.4142135623730951 //3 decimal places println((value * 1000).round / 1000.toDouble) //4 decimal places println((value * 10000).round / 10000.toDouble) 

最近我遇到了类似的问题,我用下面的方法解决了

 def round(value: Either[Double, Float], places: Int) = { if (places < 0) 0 else { val factor = Math.pow(10, places) value match { case Left(d) => (Math.round(d * factor) / factor) case Right(f) => (Math.round(f * factor) / factor) } } } def round(value: Double): Double = round(Left(value), 0) def round(value: Double, places: Int): Double = round(Left(value), places) def round(value: Float): Double = round(Right(value), 0) def round(value: Float, places: Int): Double = round(Right(value), places) 

我用这个 SO问题。 我有两个浮动\双重和隐式\显式选项的重载函数。 请注意,在重载函数的情况下,您需要明确提及返回types。

对于那些如何感兴趣,这里有一些build议的解决scheme…

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

 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: ") timed { (0 until iters) foreach { _ => scalaCustom(r.nextDouble()) } } } } 

你可以使用隐式类:

 import scala.math._ object ExtNumber extends App { implicit class ExtendedDouble(n: Double) { def rounded(x: Int) = { val w = pow(10, x) (n * w).toLong.toDouble / w } } // usage val a = 1.23456789 println(a.rounded(2)) } 

如果您关心性能,我不会使用BigDecimal。 BigDecimal将数字转换为string,然后再parsing它:

  /** Constructs a `BigDecimal` using the decimal text representation of `Double` value `d`, rounding if necessary. */ def decimal(d: Double, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(java.lang.Double.toString(d), mc), mc) 

Kaitobuild议我要坚持math操作。

有点奇怪,但很好。 我使用String而不是BigDecimal

 def round(x: Double)(p: Int): Double = { var A = x.toString().split('.') (A(0) + "." + A(1).substring(0, if (p > A(1).length()) A(1).length() else p)).toDouble }