Gibt es eine Funktion, die ein Double abschneiden oder runden kann? An einer Stelle in meinem Code möchte ich eine Zahl wie: 1.23456789gerundet werden1.23
Nachdem ich mir alle Antworten angesehen habe, denke ich, dass die kurze Antwort nein ist? :)
Marsellus Wallace
4
@Gevorg Du hast mich zum Lachen gebracht. Ich bin neu in Scala aus anderen numerikintensiven Sprachen, und mein Kiefer fiel fast auf den Boden, als ich diesen Thread las. Dies ist ein verrückter Zustand für eine Programmiersprache.
Ziemlich wahrscheinlich, würde ich sagen. Alles, was Netze oder Finanzen betrifft, kann eine Rundung und auch eine Leistung erfordern.
Rex Kerr
27
Ich nehme an, es gibt Leute, für die ein langer Anruf in einer klobigen Bibliothek verständlicher ist als einfache Mathematik. Ich würde "%.2f".format(x).toDoublein diesem Fall empfehlen . Nur 2x langsamer und Sie müssen nur eine Bibliothek verwenden, die Sie bereits kennen.
Rex Kerr
6
@RexKerr, Sie runden in diesem Fall nicht .. einfach abschneiden.
@RexKerr Ich bevorzuge Ihre string.format-Methode, aber in Gebietsschemas wie meinem (finnisch) muss darauf geachtet werden, dass das Gebietsschema ROOT korrigiert wird. ZB "% .2f" .formatLocal (java.util.Locale.ROOT, x) .toDouble. Es scheint, dass das Format aufgrund des Gebietsschemas ',' verwendet, während toDouble es nicht aufnehmen kann und eine NumberFormatException auslöst. Dies ist natürlich basiert auf dem der Code wird ausgeführt , nicht , wo es entwickelt wird .
Akauppi
77
Hier ist eine andere Lösung ohne BigDecimals
Kürzen:
(math floor 1.23456789*100)/100
Runden:
(math rint 1.23456789*100)/100
Oder für jedes doppelte n und jede Genauigkeit p:
def truncateAt(n:Double, p:Int):Double={val s = math pow (10, p);(math floor n * s)/ s }
Ähnliches gilt für die Rundungsfunktion, diesmal mit Currying:
def roundAt(p:Int)(n:Double):Double={val s = math pow (10, p);(math round n * s)/ s }
Das ist wiederverwendbarer, z. B. wenn Geldbeträge gerundet werden, könnte Folgendes verwendet werden:
Dies scheint ein falsches Ergebnis zu liefern NaN, nicht wahr?
Jangorecki
Das Problem mit floorist, dass zurückgegeben truncateAt(1.23456789, 8)wird, 1.23456788während roundAt(1.23456789, 8)der korrekte Wert von1.23456789
Todor Kolev
35
Da noch niemand den %Betreiber erwähnt hat, kommt hier. Es wird nur abgeschnitten, und Sie können sich nicht darauf verlassen, dass der Rückgabewert keine Gleitkomma-Ungenauigkeiten aufweist, aber manchmal ist es praktisch:
Ich würde dies nicht empfehlen, obwohl es meine eigene Antwort ist: Die gleichen Ungenauigkeitsprobleme, die @ryryguy im Kommentar einer anderen Antwort erwähnt hat, wirken sich auch hier aus. Verwenden Sie string.format mit dem Java ROOT-Gebietsschema (ich werde das dort kommentieren).
Akauppi
Dies ist perfekt, wenn Sie den Wert nur rendern müssen und ihn niemals in nachfolgenden Vorgängen verwenden müssen. danke
Alexander Arendar
3
Hier ist etwas Lustiges: 26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Kubudi
12
Wie wäre es mit :
val value =1.4142135623730951//3 decimal places
println((value *1000).round /1000.toDouble)//4 decimal places
println((value *10000).round /10000.toDouble)
ziemlich saubere Lösung. Hier ist meine zum Abschneiden: ((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDoublehabe es aber nicht zu oft getestet. Dieser Code würde 2 Dezimalstellen machen.
Robert
7
Bearbeiten: Das Problem, auf das @ryryguy hingewiesen hat, wurde behoben. (Vielen Dank!)
Wenn Sie möchten, dass es schnell geht, hat Kaito die richtige Idee. math.powist jedoch langsam. Für jede Standardanwendung sind Sie mit einer rekursiven Funktion besser dran:
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
}}
Dies ist ungefähr 10x schneller, wenn Sie sich im Bereich von Long(dh 18 Stellen) befinden, sodass Sie zwischen 10 ^ 18 und 10 ^ -18 runden können.
Achtung, das Multiplizieren mit dem Kehrwert funktioniert nicht zuverlässig, da es möglicherweise nicht zuverlässig als Doppel dargestellt werden kann: scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)==> res12: Double = 0.023514999999999998. Teilen Sie stattdessen durch die Bedeutung:math.round(x*100000)/100000.0
Ryryguy
Es kann auch nützlich sein, die rekursive p10Funktion durch eine Array-Suche zu ersetzen : Das Array erhöht den Speicherverbrauch um etwa 200 Byte, spart jedoch wahrscheinlich mehrere Iterationen pro Aufruf.
Levi Ramsey
4
Sie können implizite Klassen verwenden:
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))}
Das Abschneiden ist am schnellsten, gefolgt von BigDecimal. Beachten Sie, dass diese Tests mit der Ausführung von Norma Scala ohne Verwendung von Benchmarking-Tools durchgeführt wurden.
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())}}}}
Lieber scalaCustom rundet nicht ab, er schneidet nur ab
Ravinder Payal
hmm, OP war nicht spezifisch für das Runden oder Abschneiden; ...truncate or round a Double.
Cevaris
Meiner Meinung nach ist es jedoch unzureichend, die Geschwindigkeit / Ausführungszeit der Abschneidefunktion mit Rundungsfunktionen zu vergleichen. Aus diesem Grund habe ich Sie gebeten, dem Leser klar zu machen, dass die benutzerdefinierte Funktion nur abgeschnitten wird. Die von Ihnen erwähnte Funktion zum Abschneiden / Benutzerdefinieren kann weiter vereinfacht werden. val doubleParts = double. toString.split (".") doubleParts.tailHolen Sie sich jetzt die ersten beiden Zeichen von und concat mit Strings "." und doubleParts. headund analysieren, um zu verdoppeln.
Ravinder Payal
1
aktualisiert, besser aussehen? Auch Ihr Vorschlag toString.split(".")und doubleParts.head/tailVorschlag kann unter einer zusätzlichen Array-Zuweisung und einer Verkettung von Zeichenfolgen leiden. müsste allerdings testen, um sicher zu sein.
Cevaris
3
Vor kurzem hatte ich ein ähnliches Problem und habe es mit dem folgenden Ansatz gelöst
Ich habe dieses SO-Problem verwendet. Ich habe einige überladene Funktionen für Float \ Double und implizite \ explizite Optionen. Beachten Sie, dass Sie bei überladenen Funktionen den Rückgabetyp explizit angeben müssen.
Ich würde BigDecimal nicht verwenden, wenn Sie Wert auf Leistung legen. BigDecimal konvertiert Zahlen in Zeichenfolgen und analysiert sie dann erneut:
/** 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)
Ich werde mich an mathematische Manipulationen halten, wie Kaito vorgeschlagen hat.
Sie können Math.round(<double precision value> * 100.0) / 100.0
Folgendes tun: Aber Math.round ist am schnellsten, bricht jedoch in Eckfällen mit einer sehr hohen Anzahl von Dezimalstellen (z. B. rund (1000.0d, 17)) oder einem großen ganzzahligen Teil (z. B. rund (90080070060.1d, 9) schlecht zusammen. ).
Verwenden Sie Bigdecimal, es ist etwas ineffizient, da es die Werte in Zeichenfolgen konvertiert, aber mehr Erleichterung:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
Verwenden Sie den Rundungsmodus.
Wenn Sie neugierig sind und mehr darüber erfahren möchten, warum dies geschieht, können Sie Folgendes lesen:
Antworten:
Sie können verwenden
scala.math.BigDecimal
:Es gibt eine Reihe anderer Rundungsmodi , die derzeit leider nicht sehr gut dokumentiert sind (obwohl dies ihre Java-Entsprechungen sind ).
quelle
"%.2f".format(x).toDouble
in diesem Fall empfehlen . Nur 2x langsamer und Sie müssen nur eine Bibliothek verwenden, die Sie bereits kennen.scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
aberscala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.Hier ist eine andere Lösung ohne BigDecimals
Kürzen:
Runden:
Oder für jedes doppelte n und jede Genauigkeit p:
Ähnliches gilt für die Rundungsfunktion, diesmal mit Currying:
Das ist wiederverwendbarer, z. B. wenn Geldbeträge gerundet werden, könnte Folgendes verwendet werden:
quelle
NaN
, nicht wahr?floor
ist, dass zurückgegebentruncateAt(1.23456789, 8)
wird,1.23456788
währendroundAt(1.23456789, 8)
der korrekte Wert von1.23456789
Da noch niemand den
%
Betreiber erwähnt hat, kommt hier. Es wird nur abgeschnitten, und Sie können sich nicht darauf verlassen, dass der Rückgabewert keine Gleitkomma-Ungenauigkeiten aufweist, aber manchmal ist es praktisch:quelle
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Wie wäre es mit :
quelle
((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDouble
habe es aber nicht zu oft getestet. Dieser Code würde 2 Dezimalstellen machen.Bearbeiten: Das Problem, auf das @ryryguy hingewiesen hat, wurde behoben. (Vielen Dank!)
Wenn Sie möchten, dass es schnell geht, hat Kaito die richtige Idee.
math.pow
ist jedoch langsam. Für jede Standardanwendung sind Sie mit einer rekursiven Funktion besser dran:Dies ist ungefähr 10x schneller, wenn Sie sich im Bereich von
Long
(dh 18 Stellen) befinden, sodass Sie zwischen 10 ^ 18 und 10 ^ -18 runden können.quelle
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==>res12: Double = 0.023514999999999998
. Teilen Sie stattdessen durch die Bedeutung:math.round(x*100000)/100000.0
p10
Funktion durch eine Array-Suche zu ersetzen : Das Array erhöht den Speicherverbrauch um etwa 200 Byte, spart jedoch wahrscheinlich mehrere Iterationen pro Aufruf.Sie können implizite Klassen verwenden:
quelle
Für diejenigen, die interessiert sind, hier einige Zeiten für die vorgeschlagenen Lösungen ...
Das Abschneiden ist am schnellsten, gefolgt von BigDecimal. Beachten Sie, dass diese Tests mit der Ausführung von Norma Scala ohne Verwendung von Benchmarking-Tools durchgeführt wurden.
quelle
...truncate or round a Double
.doubleParts.tail
Holen Sie sich jetzt die ersten beiden Zeichen von und concat mit Strings "." unddoubleParts. head
und analysieren, um zu verdoppeln.toString.split(".")
unddoubleParts.head/tail
Vorschlag kann unter einer zusätzlichen Array-Zuweisung und einer Verkettung von Zeichenfolgen leiden. müsste allerdings testen, um sicher zu sein.Vor kurzem hatte ich ein ähnliches Problem und habe es mit dem folgenden Ansatz gelöst
Ich habe dieses SO-Problem verwendet. Ich habe einige überladene Funktionen für Float \ Double und implizite \ explizite Optionen. Beachten Sie, dass Sie bei überladenen Funktionen den Rückgabetyp explizit angeben müssen.
quelle
Die Verwendung des Scala-
f
Interpolators ist sehr einfach - https://docs.scala-lang.org/overviews/core/string-interpolation.htmlAngenommen, wir möchten auf 2 Dezimalstellen runden:
quelle
Ich würde BigDecimal nicht verwenden, wenn Sie Wert auf Leistung legen. BigDecimal konvertiert Zahlen in Zeichenfolgen und analysiert sie dann erneut:
Ich werde mich an mathematische Manipulationen halten, wie Kaito vorgeschlagen hat.
quelle
Ein bisschen seltsam, aber nett. Ich benutze String und nicht BigDecimal
quelle
Sie können
Math.round(<double precision value> * 100.0) / 100.0
Folgendes tun: Aber Math.round ist am schnellsten, bricht jedoch in Eckfällen mit einer sehr hohen Anzahl von Dezimalstellen (z. B. rund (1000.0d, 17)) oder einem großen ganzzahligen Teil (z. B. rund (90080070060.1d, 9) schlecht zusammen. ).Verwenden Sie Bigdecimal, es ist etwas ineffizient, da es die Werte in Zeichenfolgen konvertiert, aber mehr Erleichterung:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
Verwenden Sie den Rundungsmodus.Wenn Sie neugierig sind und mehr darüber erfahren möchten, warum dies geschieht, können Sie Folgendes lesen:
quelle