Scala double, et la précision
10 réponses
vous pouvez utiliser scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
il existe un certain nombre d'autres modes d'arrondi , qui ne sont malheureusement pas très bien documentés à l'heure actuelle (bien que leurs équivalents Java sont ).
Voici une autre solution sans grandes décisions
tronqué:
(math floor 1.23456789 * 100) / 100
Ronde:
(math rint 1.23456789 * 100) / 100
ou pour tout Double n et précision p:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
Similaire peut être fait pour l'arrondi de la fonction, cette fois à l'aide de nourrissage:
def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
, qui est plus réutilisable, par exemple, lorsqu'on arrondit des montants en argent, on peut utiliser ce qui suit:
def roundAt2(p: Int) = roundAt(2)(p)
puisque personne n'a encore mentionné l'opérateur %
, voici. Il ne fait que la troncature, et vous ne pouvez pas compter sur la valeur de retour pour ne pas avoir des inexactitudes de virgule flottante, mais parfois il est pratique:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
Que Diriez-vous de:
val value = 1.4142135623730951
//3 decimal places
println((value * 1000).round / 1000.toDouble)
//4 decimal places
println((value * 10000).round / 10000.toDouble)
Edit: correction du problème signalé par @ryryguy. (Merci!)
Si vous voulez qu'il soit rapide, Kaito a la bonne idée. math.pow
est lent, cependant. Pour n'importe quelle utilisation standard vous êtes mieux avec une fonction récursive:
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
}
}
c'est environ 10x plus rapide si vous êtes dans la gamme de Long
(I. e 18 chiffres), de sorte que vous pouvez tourner à n'importe où entre 10^18 et 10^-18.
vous pouvez utiliser les classes implicites:
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))
}
récemment, j'ai fait face à un problème similaire et je l'ai résolu en utilisant l'approche suivante
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)
j'ai utilisé ce DONC problème. J'ai quelques fonctions surchargées pour les options Float\Double et implicite\explicit. Notez que vous devez mentionner explicitement le type de retour en cas de surcharge de fonctions.
pour ceux qui sont intéressés, voici quelques fois pour les solutions suggérées...
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
La troncature est la plus rapide, suivie de BigDecimal. Gardez à l'esprit que ces tests ont été réalisés en exécutant l'exécution de norma scala, sans utiliser d'outils 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())
}
}
}
}
Je n'utiliserais pas BigDecimal si vous vous souciez de performance. BigDecimal convertit les nombres en chaîne et les parse à nouveau:
/** 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)
je vais m'en tenir aux manipulations mathématiques comme le suggère Kaito .
un peu étrange mais sympa. J'ai utiliser des chaînes de caractères et non pas 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
}