Después de ver todas las respuestas, supongo que la respuesta corta es no. :)
Marsellus Wallace
4
@Gevorg Me hiciste reír. Soy nuevo en Scala de otros lenguajes con muchos números y mi mandíbula casi golpea el suelo al leer este hilo. Este es un estado de locura para un lenguaje de programación.
Es bastante probable, diría yo. Cualquier cosa que involucre cuadrículas o finanzas puede requerir redondeo y también rendimiento.
Rex Kerr
28
Supongo que hay personas para quienes una larga llamada a una biblioteca torpe es más comprensible que las simples matemáticas. Lo recomendaría "%.2f".format(x).toDoubleen ese caso. Solo 2 veces más lento, y solo tiene que usar una biblioteca que ya conoce.
Rex Kerr
6
@RexKerr, no está redondeando en este caso ... simplemente truncando.
@RexKerr Prefiero su forma de string.format, pero en las configuraciones regionales como la mía (finlandés), se debe tener cuidado para corregir la configuración regional ROOT. Por ejemplo, "% .2f" .formatLocal (java.util.Locale.ROOT, x) .toDouble. Parece que el formato usa ',' debido a la configuración regional, mientras que toDouble no puede aceptarlo y arroja una NumberFormatException. Por supuesto, esto se basa en el lugar donde se ejecuta su código , no donde se desarrolla.
akauppi
77
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:
esto parece devolver un resultado incorrecto NaN, ¿no es así?
Jangorecki
el problema floores que truncateAt(1.23456789, 8)volverá 1.23456788mientras roundAt(1.23456789, 8)que devolverá el valor correcto de1.23456789
Todor Kolev
35
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:
No recomendaría esto, aunque es mi propia respuesta: los mismos problemas de inexactitud mencionados por @ryryguy en el comentario de otra respuesta también afectan aquí. Use string.format con la configuración regional Java ROOT (comentaré sobre eso allí).
akauppi
esto es perfecto si solo necesita representar el valor y nunca usarlo en operaciones posteriores. gracias
Alexander Arendar
3
aquí hay algo divertido: 26.257391515826225 - 0.057391515826223094 = 26.200000000000003
kubudi
12
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)
solución bastante limpia. Aquí está el mío para el truncamiento: ((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDoubleaunque no lo probé demasiado. Este código ocuparía 2 lugares decimales.
robert
7
Editar: solucionó el problema que señaló @ryryguy. (¡Gracias!)
Si quieres que sea rápido, Kaito tiene la idea correcta. math.powaunque 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.
Cuidado, multiplicar por el recíproco no funciona de manera confiable, porque puede no ser representable de manera confiable como un doble: 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
ryryguy
También puede ser útil reemplazar la p10funció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.
Levi Ramsey
4
Puede usar clases implícitas:
import scala.math._
objectExtNumberextendsApp{implicitclassExtendedDouble(n:Double){def rounded(x:Int)={val w = pow(10, x)(n * w).toLong.toDouble / w
}}// usageval a =1.23456789
println(a.rounded(2))}
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.
objectTestFormatters{val r = scala.util.Randomdef 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 =2val 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())}}}}
Estimado scalaCustom no se está redondeando, solo se está truncando
Ravinder Payal
hmm, OP no era específico para redondear o truncar; ...truncate or round a Double.
cevaris
Pero en mi opinión, comparar la velocidad / tiempo de ejecución de la función de truncar con las funciones de redondeo es inadecuado. Por eso le pedí que le aclarara al lector que la función personalizada solo se trunca. Y la función truncar / personalizada mencionada por usted se puede simplificar aún más. val doubleParts = doble. toString.split (".") Ahora obtenga los dos primeros caracteres de doubleParts.taily concat con cadenas "." y doubleParts. heady analizar para duplicar.
Ravinder Payal
1
actualizado, se ve mejor? también su sugerencia toString.split(".")y doubleParts.head/tailsugerencia pueden sufrir una asignación de matriz adicional más la concatenación de cadenas. Sin embargo, necesitaría probarlo para estar seguro.
cevaris
3
Recientemente, enfrenté un problema similar y lo resolví usando el siguiente enfoque
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.
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=newBigDecimal(newBigDec(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:
Respuestas:
Puede utilizar
scala.math.BigDecimal
: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 ).
fuente
"%.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
peroscala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.Aquí hay otra solución sin BigDecimals
Truncar:
Redondo:
O para cualquier doble n y precisión p:
Se puede hacer algo similar para la función de redondeo, esta vez usando curado:
que es más reutilizable, por ejemplo, al redondear cantidades de dinero, se podría usar lo siguiente:
fuente
NaN
, ¿no es así?floor
es quetruncateAt(1.23456789, 8)
volverá1.23456788
mientrasroundAt(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:fuente
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Qué tal si :
fuente
((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: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.fuente
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:
fuente
Para aquellos que estén interesados, aquí hay algunos momentos para las soluciones sugeridas ...
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.
fuente
...truncate or round a Double
.doubleParts.tail
y concat con cadenas "." ydoubleParts. head
y analizar para duplicar.toString.split(".")
ydoubleParts.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
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.
fuente
En realidad, es muy fácil de manejar con el
f
interpolador Scala . https://docs.scala-lang.org/overviews/core/string-interpolation.htmlSupongamos que queremos redondear hasta 2 decimales:
fuente
No usaría BigDecimal si te importa el rendimiento. BigDecimal convierte números en cadenas y luego los vuelve a analizar:
Voy a ceñirme a las manipulaciones matemáticas como sugirió Kaito .
fuente
Un poco extraño pero agradable. Yo uso String y no BigDecimal
fuente
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:
fuente