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
Après avoir examiné toutes les réponses, je suppose que la réponse courte est non? :)
Marsellus Wallace
4
@Gevorg Vous m'avez fait rire. Je suis nouveau sur Scala à partir d'autres langages à forte densité numérique, et ma mâchoire a presque touché le sol en lisant ce fil. C'est un état de choses insensé pour un langage de programmation.
Très probable, je dirais. Tout ce qui concerne les grilles ou la finance peut nécessiter des arrondis et également des performances.
Rex Kerr
28
Je suppose qu'il y a des gens pour qui un long appel à une bibliothèque maladroite est plus compréhensible que de simples mathématiques. Je recommanderais "%.2f".format(x).toDoubledans ce cas. Seulement 2x plus lentement, et vous n'avez qu'à utiliser une bibliothèque que vous connaissez déjà.
Rex Kerr
6
@RexKerr, vous n'êtes pas arrondi dans ce cas ... simplement tronqué.
@RexKerr Je préfère votre manière string.format, mais dans les locales sa mienne (finnois), il faut prendre soin de corriger la locale ROOT. Par exemple, "% .2f" .formatLocal (java.util.Locale.ROOT, x) .toDouble. Il semble que le format utilise ',' à cause de la locale alors que toDouble n'est pas capable de le prendre et lève une NumberFormatException. Cela dépend bien sûr de l'endroit où votre code est exécuté , pas de l'endroit où il a été développé.
akauppi
77
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:
roundAt2 doit être def roundAt2 (n: Double) = roundAt (2) (n) no?
C4stor
cela semble retourner un résultat incorrect pour NaN, n'est-ce pas?
jangorecki
le problème floorest quetruncateAt(1.23456789, 8) retournera 1.23456788tandis que roundAt(1.23456789, 8)retournera la valeur correcte de1.23456789
Todor Kolev
35
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:
Je ne le recommanderais pas, même si c'est ma propre réponse: les mêmes problèmes d'inexactitude que ceux mentionnés par @ryryguy dans le commentaire d'une autre réponse affectent également ici. Utilisez string.format avec la locale Java ROOT (je vais commenter là-dessus).
akauppi
c'est parfait si vous avez juste besoin de rendre la valeur et de ne jamais l'utiliser dans les opérations suivantes. merci
Alexander Arendar
3
voici quelque chose de drôle: 26.257391515826225 - 0.057391515826223094 = 26.200000000000003
kubudi
12
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)
solution assez propre. Voici le mien pour la troncature: ((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDoubleje ne l'ai pas trop testé. Ce code ferait 2 décimales.
robert
7
Edit: correction du problème signalé par @ryryguy. (Merci!)
Si vous voulez que ce soit rapide, Kaito a la bonne idée. math.powest 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.
Attention, la multiplication par la réciproque ne fonctionne pas de manière fiable, car elle peut ne pas être représentable de manière fiable comme un double: 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
ryryguy
Il peut également être utile de remplacer la p10fonction 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.
Levi Ramsey
4
Vous pouvez utiliser des classes implicites:
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))}
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.
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())}}}}
Cher scalaCustom n'est pas d'arrondi, il est juste tronqué
Ravinder Payal
hmm, OP n'était pas spécifique à l'arrondi ou à la troncature; ...truncate or round a Double.
cevaris
Mais à mon avis, comparer la vitesse / le temps d'exécution de la fonction de troncature avec les fonctions d'arrondi est inadéquat. C'est pourquoi je vous ai demandé de préciser au lecteur que la fonctionnalité personnalisée ne fait que tronquer. Et la fonction tronquée / personnalisée mentionnée par vous peut être simplifiée davantage. val doubleParts = double. toString.split (".") Maintenant, récupérez les deux premiers caractères de doubleParts.tailet concattez avec les chaînes "." et doubleParts. headet analyser pour doubler.
Ravinder Payal
1
mis à jour, une meilleure apparence? votre suggestion toString.split(".")et doubleParts.head/tailsuggestion 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.
cevaris
3
Récemment, j'ai rencontré un problème similaire et je l'ai résolu en utilisant l'approche suivante
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.
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=newBigDecimal(newBigDec(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:
Réponses:
Vous pouvez utiliser
scala.math.BigDecimal
: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 ).
la source
"%.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
maisscala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.Voici une autre solution sans BigDecimals
Tronquer:
Rond:
Ou pour tout double n et précision p:
La même chose peut être faite pour la fonction d'arrondi, cette fois en utilisant le curry:
qui est plus réutilisable, par exemple pour arrondir des montants d'argent, les éléments suivants peuvent être utilisés:
la source
NaN
, n'est-ce pas?floor
est quetruncateAt(1.23456789, 8)
retournera1.23456788
tandis queroundAt(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:la source
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Que diriez-vous :
la source
((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: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.la source
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:
la source
Pour ceux qui sont intéressés, voici quelques fois les solutions proposées ...
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.
la source
...truncate or round a Double
.doubleParts.tail
et concattez avec les chaînes "." etdoubleParts. head
et analyser pour doubler.toString.split(".")
etdoubleParts.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
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.
la source
C'est en fait très facile à manipuler avec l'
f
interpolateur Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.htmlSupposons que nous voulions arrondir à 2 décimales:
la source
Je n'utiliserais pas BigDecimal si vous vous souciez des performances. BigDecimal convertit les nombres en chaîne, puis les analyse à nouveau:
Je vais m'en tenir aux manipulations mathématiques comme Kaito l'a suggéré.
la source
Un peu étrange mais sympa. J'utilise String et non BigDecimal
la source
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:
la source