scala double truncate rounding

Scala duplica y precisión



double truncate (10)

Aquí hay otra solución sin BigDecimals

Truncar:

(math floor 1.23456789 * 100) / 100

Redondo:

(math rint 1.23456789 * 100) / 100

O para cualquier n doble y precisión p:

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

Similar se puede hacer para la función de redondeo, esta vez usando currying:

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

que es más reutilizable, por ejemplo, al redondear cantidades de dinero, se puede usar lo siguiente:

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

¿Hay alguna función que pueda truncar o redondear un doble? En un punto de mi código me gustaría que un número como: 1.23456789 a 1.23


Como nadie mencionó el operador % todavía, aquí viene. Solo realiza el truncamiento, y no puede confiar en que el valor de retorno no tenga imprecisiones en coma flotante, pero a veces es útil:

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


Editar: solucionó el problema que @ryryguy señaló. (¡Gracias!)

Si quieres que sea rápido, Kaito tiene la idea correcta. math.pow es lento, sin embargo. Para cualquier uso estándar, está mejor con una función recursiva:

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

Esto es aproximadamente 10 veces más rápido si está dentro del rango de Long (es decir, 18 dígitos), por lo que puede redondear en cualquier lugar entre 10 ^ 18 y 10 ^ -18.


No usaría BigDecimal si te preocupa el rendimiento. BigDecimal convierte números en cadena y luego los analiza de nuevo:

/** 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)

Voy a seguir con las manipulaciones matemáticas como sugirió Kaito .


Para quienes están interesados, aquí hay algunas veces para las soluciones sugeridas ...

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

El truncamiento es el más rápido, seguido de BigDecimal. Tenga en cuenta que estas pruebas se realizaron ejecutando la ejecución norma scala, sin utilizar ninguna herramienta de evaluación comparativa.

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



Puedes usar clases implícitas:

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


Qué tal si :

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


Recientemente, me enfrenté a un problema similar y lo resolví utilizando el siguiente enfoque

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)

Usé this problema SO. Tengo un par de funciones sobrecargadas para las opciones Float / Double e implicit / explicit. Tenga en cuenta que, debe mencionar explícitamente el tipo de devolución en caso de funciones sobrecargadas.


Un poco extraño pero agradable. Yo uso String y no 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 }