Existe-t-il une fonction qui peut tronquer ou arrondir un double? À un moment donné dans mon code, je voudrais un nombre comme: 1.23456789
être arrondi à1.23
Existe-t-il une fonction qui peut tronquer ou arrondir un double? À un moment donné dans mon code, je voudrais un nombre comme: 1.23456789
être arrondi à1.23
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 le soient ).
"%.2f".format(x).toDouble
dans ce cas. Seulement 2x plus lentement, et vous n'avez qu'à utiliser une bibliothèque que vous connaissez déjà.
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
mais scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.
Voici une autre solution sans BigDecimals
Tronquer:
(math floor 1.23456789 * 100) / 100
Rond:
(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 }
La même chose peut être faite pour la fonction d'arrondi, cette fois en utilisant le curry:
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 pour arrondir des montants d'argent, les éléments suivants peuvent être utilisés:
def roundAt2(n: Double) = roundAt(2)(n)
NaN
, n'est-ce pas?
floor
est quetruncateAt(1.23456789, 8)
retournera 1.23456788
tandis que roundAt(1.23456789, 8)
retournera la valeur correcte de1.23456789
Puisque personne n'a encore mentionné l' %
opérateur, voici. Cela ne fait que la troncature et vous ne pouvez pas vous fier à la valeur de retour pour ne pas avoir d'inexactitudes en virgule flottante, mais parfois c'est pratique:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Que diriez-vous :
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
je ne l'ai pas trop testé. Ce code ferait 2 décimales.
Edit: correction du problème signalé par @ryryguy. (Merci!)
Si vous voulez que ce soit rapide, Kaito a la bonne idée. math.pow
est lent, cependant. Pour toute utilisation standard, il vaut mieux utiliser 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 10 fois plus rapide si vous êtes dans la plage de Long
(c'est-à-dire 18 chiffres), vous pouvez donc arrondir n'importe où entre 10 ^ 18 et 10 ^ -18.
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==> res12: Double = 0.023514999999999998
. Divisez plutôt par la signification:math.round(x*100000)/100000.0
p10
fonction récursive par une recherche de tableau: le tableau augmentera la consommation de mémoire d'environ 200 octets mais économisera probablement plusieurs itérations par appel.
Vous pouvez utiliser des 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))
}
Pour ceux qui sont intéressés, voici quelques fois les solutions proposé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é effectués en exécutant une exécution norma scala, sans utiliser d'outils d'analyse comparative.
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
et concattez avec les chaînes "." et doubleParts. head
et analyser pour doubler.
toString.split(".")
et doubleParts.head/tail
suggestion peuvent également souffrir d'une allocation supplémentaire de tableau et d'une concaténation de chaînes. aurait besoin de tester pour en être sûr.
Récemment, j'ai rencontré 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 problème SO. J'ai quelques fonctions surchargées pour les options Float \ Double et implicites \ explicites. Notez que vous devez mentionner explicitement le type de retour en cas de fonctions surchargées.
C'est en fait très facile à manipuler avec l' f
interpolateur Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.html
Supposons que nous voulions arrondir à 2 décimales:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
Je n'utiliserais pas BigDecimal si vous vous souciez des performances. BigDecimal convertit les nombres en chaîne, puis les analyse à 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 Kaito l'a suggéré.
Vous pouvez faire: Math.round(<double precision value> * 100.0) / 100.0
Mais Math.round est le plus rapide mais il se décompose mal dans les cas d'angle avec un nombre très élevé de décimales (par exemple, round (1000.0d, 17)) ou une grande partie entière (par exemple, round (90080070060.1d, 9) ).
Utilisez Bigdecimal, c'est un peu inefficace car il convertit les valeurs en chaîne mais plus fiable:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
utilisez votre préférence pour le mode Arrondi.
Si vous êtes curieux et que vous voulez savoir plus en détail pourquoi cela se produit, vous pouvez lire ceci: