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())
}
}
}
}
Puede usar scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
Hay una serie de otros modos de redondeo , que desafortunadamente no están muy bien documentados en la actualidad (aunque sus equivalentes Java son ).
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
}