Esiste una funzione che può troncare o arrotondare un Double? A un certo punto del mio codice vorrei che un numero del tipo: 1.23456789
fosse arrotondato a1.23
Esiste una funzione che può troncare o arrotondare un Double? A un certo punto del mio codice vorrei che un numero del tipo: 1.23456789
fosse arrotondato a1.23
Risposte:
Puoi usare scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
Ci sono un certo numero di altre modalità di arrotondamento , che sfortunatamente non sono molto ben documentate al momento (sebbene i loro equivalenti Java lo siano ).
"%.2f".format(x).toDouble
in quel caso. Solo 2 volte più lento e devi usare solo una libreria che già conosci.
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
ma scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.
Ecco un'altra soluzione senza BigDecimals
Troncare:
(math floor 1.23456789 * 100) / 100
Il giro:
(math rint 1.23456789 * 100) / 100
O per qualsiasi doppia ne precisione p:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
Simile può essere fatto per la funzione di arrotondamento, questa volta usando il curry:
def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
che è più riutilizzabile, ad esempio quando si arrotondano gli importi in denaro si potrebbe utilizzare quanto segue:
def roundAt2(n: Double) = roundAt(2)(n)
NaN
, non è vero?
floor
è che truncateAt(1.23456789, 8)
tornerà 1.23456788
mentre roundAt(1.23456789, 8)
restituirà il valore corretto di1.23456789
Dato che nessuno ha %
ancora menzionato l' operatore, ecco che arriva. Esegue solo il troncamento e non puoi fare affidamento sul valore restituito per non avere imprecisioni in virgola mobile, ma a volte è utile:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Che ne dite di :
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
non l'ho testato troppo però. Questo codice farebbe 2 cifre decimali.
Modifica: risolto il problema segnalato da @ryryguy. (Grazie!)
Se vuoi che sia veloce, Kaito ha l'idea giusta. math.pow
è lento, però. Per qualsiasi uso standard è meglio con una funzione ricorsiva:
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
}
}
Questo è circa 10 volte più veloce se ti trovi nell'intervallo di Long
(cioè 18 cifre), quindi puoi arrotondare ovunque tra 10 ^ 18 e 10 ^ -18.
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==> res12: Double = 0.023514999999999998
. Dividi invece per il significato:math.round(x*100000)/100000.0
p10
funzione ricorsiva con una ricerca di array: l'array aumenterà il consumo di memoria di circa 200 byte ma probabilmente salverà diverse iterazioni per chiamata.
Puoi usare classi implicite:
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))
}
Per chi è interessato, ecco alcune volte per le soluzioni suggerite ...
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
Il troncamento è il più veloce, seguito da BigDecimal. Tieni presente che questi test sono stati eseguiti eseguendo un'esecuzione normale scala, senza utilizzare strumenti di 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 concatena con le stringhe "." e doubleParts. head
e analizza per raddoppiare.
toString.split(".")
e doubleParts.head/tail
suggerimento potrebbe risentire dell'allocazione di array extra più la concatenazione di stringhe. avrebbe bisogno di fare un test per essere sicuro però.
Recentemente, ho affrontato un problema simile e l'ho risolto utilizzando il seguente approccio
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)
Ho usato questo problema SO. Ho un paio di funzioni sovraccaricate per entrambe le opzioni Float \ Double e implicit \ explicit. Si noti che è necessario menzionare esplicitamente il tipo restituito in caso di funzioni sovraccaricate.
In realtà è molto facile da gestire utilizzando Scala f
interpolator - https://docs.scala-lang.org/overviews/core/string-interpolation.html
Supponiamo di voler arrotondare fino a 2 cifre decimali:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
Non userei BigDecimal se ti interessano le prestazioni. BigDecimal converte i numeri in stringa e quindi li analizza nuovamente:
/** 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)
Mi atterrò alle manipolazioni matematiche come suggerito da Kaito .
Tu puoi fare:Math.round(<double precision value> * 100.0) / 100.0
Ma Math.round è più veloce ma si rompe male nei casi d'angolo con un numero molto alto di cifre decimali (ad esempio round (1000.0d, 17)) o una parte intera grande (ad esempio round (90080070060.1d, 9) ).
Usa Bigdecimal è un po 'inefficiente in quanto converte i valori in stringa ma più pertinente:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
usa la tua preferenza per la modalità Arrotondamento.
Se sei curioso e vuoi sapere più in dettaglio perché questo accade puoi leggere questo: