Apakah ada fungsi yang dapat memotong atau membulatkan Double? Pada satu titik dalam kode saya, saya ingin angka seperti: 1.23456789
dibulatkan menjadi1.23
Apakah ada fungsi yang dapat memotong atau membulatkan Double? Pada satu titik dalam kode saya, saya ingin angka seperti: 1.23456789
dibulatkan menjadi1.23
Jawaban:
Anda dapat menggunakan scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
Ada sejumlah mode pembulatan lainnya , yang sayangnya tidak terdokumentasi dengan baik saat ini (meskipun padanan Java mereka ).
"%.2f".format(x).toDouble
dalam kasus itu. Hanya 2x lebih lambat, dan Anda hanya perlu menggunakan perpustakaan yang sudah Anda kenal.
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
tapi scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.
Inilah solusi lain tanpa BigDecimals
Memotong:
(math floor 1.23456789 * 100) / 100
Bulat:
(math rint 1.23456789 * 100) / 100
Atau untuk p ganda dan presisi:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
Hal serupa dapat dilakukan untuk fungsi pembulatan, kali ini menggunakan kari:
def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
mana yang lebih dapat digunakan kembali, misalnya ketika pembulatan jumlah uang berikut ini dapat digunakan:
def roundAt2(n: Double) = roundAt(2)(n)
NaN
, bukan?
floor
adalah yang truncateAt(1.23456789, 8)
akan kembali 1.23456788
sementara roundAt(1.23456789, 8)
akan mengembalikan nilai yang benar dari1.23456789
Karena belum ada yang menyebutkan %
operatornya, ini dia. Ini hanya melakukan pemotongan, dan Anda tidak dapat mengandalkan nilai pengembalian untuk tidak memiliki ketidakakuratan floating point, tetapi terkadang berguna:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Bagaimana tentang :
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
tidak mengujinya terlalu banyak. Kode ini akan melakukan 2 tempat desimal.
Edit: perbaiki masalah yang ditunjukkan @ryryguy. (Terima kasih!)
Kalau mau cepat, Kaito punya ide yang tepat. math.pow
lambat. Untuk penggunaan standar apa pun, Anda lebih baik menggunakan fungsi rekursif:
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
}
}
Ini sekitar 10x lebih cepat jika Anda berada dalam kisaran Long
(yaitu 18 digit), sehingga Anda dapat membulatkan di mana saja antara 10 ^ 18 dan 10 ^ -18.
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==> res12: Double = 0.023514999999999998
. Bagilah dengan signifikansi sebagai gantinya:math.round(x*100000)/100000.0
p10
fungsi rekursif dengan pencarian array: array akan meningkatkan konsumsi memori sekitar 200 byte tetapi kemungkinan akan menghemat beberapa iterasi per panggilan.
Anda dapat menggunakan kelas implisit:
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))
}
Bagi yang tertarik, berikut adalah beberapa solusi yang disarankan ...
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
Pemotongan adalah yang tercepat, diikuti oleh BigDecimal. Perlu diingat pengujian ini dilakukan dengan menjalankan norma scala execution, tidak menggunakan alat benchmarking apapun.
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
dan concat dengan string "." dan doubleParts. head
dan parse menjadi double.
toString.split(".")
dan doubleParts.head/tail
saran Anda mungkin mengalami alokasi array tambahan ditambah penggabungan string. perlu menguji untuk memastikannya.
Baru-baru ini, saya menghadapi masalah serupa dan saya menyelesaikannya dengan menggunakan pendekatan berikut
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)
Saya menggunakan masalah SO ini . Saya memiliki beberapa fungsi yang kelebihan beban untuk opsi Float \ Double dan implisit \ eksplisit. Perhatikan bahwa, Anda perlu menyebutkan secara eksplisit jenis kembalian jika fungsi kelebihan beban.
Sebenarnya sangat mudah untuk menangani menggunakan f
interpolator Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.html
Misalkan kita ingin membulatkan hingga 2 tempat desimal:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
Saya tidak akan menggunakan BigDecimal jika Anda peduli dengan kinerja. BigDecimal mengonversi angka menjadi string dan kemudian menguraikannya kembali:
/** 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)
Saya akan tetap berpegang pada manipulasi matematika seperti yang disarankan Kaito .
Anda dapat melakukan: Math.round(<double precision value> * 100.0) / 100.0
Tetapi Math.round adalah yang tercepat tetapi rusak parah dalam kasus sudut dengan jumlah tempat desimal yang sangat tinggi (misalnya bulat (1000.0d, 17)) atau bagian bilangan bulat besar (misalnya round (90080070060.1d, 9) ).
Gunakan Desimal Besar, ini agak tidak efisien karena mengubah nilai menjadi string tetapi lebih relieval:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
gunakan preferensi mode Rounding Anda.
Jika Anda penasaran dan ingin mengetahui lebih detail mengapa hal ini terjadi, Anda dapat membaca ini: