¿Existe una función que pueda truncar o redondear un Double? En un punto de mi código, me gustaría que un número como: 1.23456789
se redondee a1.23
¿Existe una función que pueda truncar o redondear un Double? En un punto de mi código, me gustaría que un número como: 1.23456789
se redondee a1.23
Respuestas:
Puede utilizar scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
Hay varios otros modos de redondeo , que desafortunadamente no están muy bien documentados en la actualidad (aunque sus equivalentes de Java sí lo están ).
"%.2f".format(x).toDouble
en ese caso. Solo 2 veces más lento, y solo tiene que usar una biblioteca que ya conoce.
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
pero scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.
Aquí hay otra solución sin BigDecimals
Truncar:
(math floor 1.23456789 * 100) / 100
Redondo:
(math rint 1.23456789 * 100) / 100
O para cualquier doble n y precisión p:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
Se puede hacer algo similar para la función de redondeo, esta vez usando curado:
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 podría usar lo siguiente:
def roundAt2(n: Double) = roundAt(2)(n)
NaN
, ¿no es así?
floor
es que truncateAt(1.23456789, 8)
volverá 1.23456788
mientras roundAt(1.23456789, 8)
que devolverá el valor correcto de1.23456789
Como nadie mencionó al %
operador todavía, aquí viene. Solo trunca, y no puede confiar en que el valor de retorno no tenga inexactitudes de punto flotante, pero a veces es útil:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
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)
((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDouble
aunque no lo probé demasiado. Este código ocuparía 2 lugares decimales.
Editar: solucionó el problema que señaló @ryryguy. (¡Gracias!)
Si quieres que sea rápido, Kaito tiene la idea correcta. math.pow
aunque es lento. Para cualquier uso estándar, es mejor que tenga 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.
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==> res12: Double = 0.023514999999999998
. Dividir por el significado en su lugar:math.round(x*100000)/100000.0
p10
función recursiva con una búsqueda de matriz: la matriz aumentará el consumo de memoria en aproximadamente 200 bytes, pero probablemente ahorrará varias iteraciones por llamada.
Puede 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))
}
Para aquellos que estén interesados, aquí hay algunos momentos 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 de 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())
}
}
}
}
...truncate or round a Double
.
doubleParts.tail
y concat con cadenas "." y doubleParts. head
y analizar para duplicar.
toString.split(".")
y doubleParts.head/tail
sugerencia pueden sufrir una asignación de matriz adicional más la concatenación de cadenas. Sin embargo, necesitaría probarlo para estar seguro.
Recientemente, enfrenté un problema similar y lo resolví usando 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)
He utilizado este tema SO. Tengo un par de funciones sobrecargadas para las opciones Float \ Double e implícita \ explícita. Tenga en cuenta que debe mencionar explícitamente el tipo de retorno en caso de funciones sobrecargadas.
En realidad, es muy fácil de manejar con el f
interpolador Scala . https://docs.scala-lang.org/overviews/core/string-interpolation.html
Supongamos que queremos redondear hasta 2 decimales:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
No usaría BigDecimal si te importa el rendimiento. BigDecimal convierte números en cadenas y luego los vuelve a analizar:
/** 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 ceñirme a las manipulaciones matemáticas como sugirió Kaito .
Puede hacer: Math.round(<double precision value> * 100.0) / 100.0
Pero Math.round es más rápido pero se descompone mal en los casos de esquina con un número muy alto de lugares decimales (por ejemplo, round (1000.0d, 17)) o una parte entera grande (por ejemplo, round (90080070060.1d, 9) ).
Use Bigdecimal, es un poco ineficiente, ya que convierte los valores en una cadena, pero más relevación:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
use su preferencia del modo Redondeo.
Si tienes curiosidad y quieres saber más detalles por qué sucede esto puedes leer esto: