Existe uma função que pode truncar ou arredondar um Double? Em um ponto do meu código, gostaria de um número como: 1.23456789
ser arredondado para1.23
Existe uma função que pode truncar ou arredondar um Double? Em um ponto do meu código, gostaria de um número como: 1.23456789
ser arredondado para1.23
Respostas:
Você pode usar scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
Existem vários outros modos de arredondamento , que infelizmente não estão muito bem documentados no momento (embora seus equivalentes em Java estejam ).
"%.2f".format(x).toDouble
nesse caso. Apenas 2x mais lento e você só precisa usar uma biblioteca que já conhece.
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
mas scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.
Aqui está outra solução sem BigDecimals
Truncar:
(math floor 1.23456789 * 100) / 100
Volta:
(math rint 1.23456789 * 100) / 100
Ou para qualquer duplo n e precisão p:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
Semelhante pode ser feito para a função de arredondamento, desta vez usando currying:
def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
que é mais reutilizável, por exemplo, ao arredondar valores em dinheiro, o seguinte pode ser usado:
def roundAt2(n: Double) = roundAt(2)(n)
NaN
, não é?
floor
é que truncateAt(1.23456789, 8)
retornará 1.23456788
enquanto roundAt(1.23456789, 8)
retornará o valor correto de1.23456789
Como ninguém mencionou a %
operadora ainda, aí vem. Ele apenas faz o truncamento e você não pode contar com o valor de retorno para não ter imprecisões de ponto flutuante, mas às vezes é útil:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
E se :
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
mas não testei muito. Este código faria 2 casas decimais.
Edit: corrigido o problema que @ryryguy apontou. (Obrigado!)
Se você quer que seja rápido, Kaito tem a ideia certa. math.pow
é lento, no entanto. Para qualquer uso padrão, é melhor você usar uma função 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
}
}
Isso é cerca de 10x mais rápido se você estiver na faixa de Long
(ou seja, 18 dígitos), então você pode arredondar em qualquer lugar entre 10 ^ 18 e 10 ^ -18.
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==> res12: Double = 0.023514999999999998
. Em vez disso, divida pelo significado:math.round(x*100000)/100000.0
p10
função recursiva por uma pesquisa de array: o array aumentará o consumo de memória em cerca de 200 bytes, mas provavelmente salvará várias iterações por chamada.
Você pode usar classes 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 os interessados, aqui ficam alguns horários para as soluções sugeridas ...
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
O truncamento é o mais rápido, seguido por BigDecimal. Lembre-se de que esses testes foram feitos executando a execução do norma scala, sem usar nenhuma ferramenta de benchmarking.
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
e concat com as strings "." and doubleParts. head
and parse para dobrar.
toString.split(".")
e doubleParts.head/tail
sugestão podem sofrer de alocação de array extra mais concatenação de string. precisaria testar para ter certeza.
Recentemente, enfrentei um problema semelhante e resolvi usando a seguinte abordagem
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)
Eu usei esse problema do SO. Eu tenho algumas funções sobrecarregadas para opções Float \ Double e implícita \ explícita. Observe que você precisa mencionar explicitamente o tipo de retorno no caso de funções sobrecarregadas.
Na verdade, é muito fácil de manusear usando o f
interpolador Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.html
Suponha que queremos arredondar até 2 casas decimais:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
Eu não usaria BigDecimal se você se preocupa com desempenho. BigDecimal converte números em string e, em seguida, analisa novamente:
/** 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)
Vou me limitar às manipulações matemáticas, conforme sugerido por Kaito .
Você pode fazer: Math.round(<double precision value> * 100.0) / 100.0
Mas Math.round é mais rápido, mas se decompõe mal em casos de canto com um número muito alto de casas decimais (por exemplo, redondo (1000.0d, 17)) ou parte inteira grande (por exemplo, redondo (90080070060.1d, 9) )
Use Bigdecimal, é um pouco ineficiente, pois converte os valores em string, mas mais alívio:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
use o modo de arredondamento de sua preferência.
Se você estiver curioso e quiser saber mais detalhes por que isso acontece, você pode ler isto: