Apakah ada fungsi yang dapat memotong atau membulatkan Double? Pada satu titik dalam kode saya, saya ingin angka seperti: 1.23456789dibulatkan menjadi1.23
Setelah melihat semua jawaban, saya rasa jawaban singkatnya adalah tidak? :)
Marsellus Wallace
4
@Gevorg Anda membuat saya tertawa. Saya baru mengenal Scala dari bahasa numerik-berat lainnya, dan rahang saya hampir menyentuh lantai saat membaca utas ini. Ini adalah urusan yang gila untuk bahasa pemrograman.
Sangat mungkin, menurutku. Apa pun yang melibatkan jaringan atau keuangan dapat memerlukan pembulatan dan juga kinerja.
Rex Kerr
28
Saya kira ada orang yang panggilan lama ke perpustakaan kikuk lebih bisa dipahami daripada matematika sederhana. Saya akan merekomendasikan "%.2f".format(x).toDoubledalam kasus itu. Hanya 2x lebih lambat, dan Anda hanya perlu menggunakan perpustakaan yang sudah Anda kenal.
Rex Kerr
6
@RexKerr, Anda tidak melakukan pembulatan dalam kasus ini .. hanya memotong.
@RexKerr Saya lebih suka cara string.format Anda, tetapi di lokal sa saya (Finlandia), hati-hati harus dilakukan untuk memperbaiki ke lokal ROOT. Misalnya "% .2f" .formatLocal (java.util.Locale.ROOT, x) .toDouble. Tampaknya, format menggunakan ',' karena lokal sedangkan toDouble tidak dapat menerimanya dan melontarkan NumberFormatException. Ini tentu saja didasarkan pada tempat kode Anda dijalankan , bukan di mana kode itu dikembangkan.
akauppi
77
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:
ini sepertinya mengembalikan hasil yang salah NaN, bukan?
jangorecki
Masalahnya flooradalah yang truncateAt(1.23456789, 8)akan kembali 1.23456788sementara roundAt(1.23456789, 8)akan mengembalikan nilai yang benar dari1.23456789
Todor Kolev
35
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:
Tidak akan merekomendasikan ini meskipun itu jawaban saya sendiri: masalah ketidakakuratan yang sama seperti yang disebutkan oleh @ryryguy dalam komentar jawaban lain juga memengaruhi di sini. Gunakan string.format dengan lokal ROOT Java (saya akan berkomentar tentang itu di sana).
akauppi
ini sempurna jika Anda hanya perlu merender nilainya dan tidak pernah menggunakannya dalam operasi selanjutnya. terima kasih
Alexander Arendar
3
ini sesuatu yang lucu: 26.257391515826225 - 0.057391515826223094 = 26.200000000000003
kubudi
12
Bagaimana tentang :
val value =1.4142135623730951//3 decimal places
println((value *1000).round /1000.toDouble)//4 decimal places
println((value *10000).round /10000.toDouble)
solusi yang cukup bersih. Ini milik saya untuk pemotongan: ((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDoubletidak mengujinya terlalu banyak. Kode ini akan melakukan 2 tempat desimal.
robert
7
Edit: perbaiki masalah yang ditunjukkan @ryryguy. (Terima kasih!)
Kalau mau cepat, Kaito punya ide yang tepat. math.powlambat. 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.
Hati-hati, mengalikan dengan timbal balik tidak bekerja dengan andal, karena mungkin tidak dapat direpresentasikan secara andal sebagai penggandaan: 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
ryryguy
Mungkin juga berguna untuk mengganti p10fungsi rekursif dengan pencarian array: array akan meningkatkan konsumsi memori sekitar 200 byte tetapi kemungkinan akan menghemat beberapa iterasi per panggilan.
Levi Ramsey
4
Anda dapat menggunakan kelas implisit:
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))}
Pemotongan adalah yang tercepat, diikuti oleh BigDecimal. Perlu diingat pengujian ini dilakukan dengan menjalankan norma scala execution, tidak menggunakan alat benchmarking apapun.
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())}}}}
ScalaCustom yang terhormat tidak membulatkan, itu hanya memotong
Ravinder Payal
hmm, OP tidak spesifik untuk pembulatan atau pemotongan; ...truncate or round a Double.
cevaris
Namun menurut saya, membandingkan kecepatan / waktu eksekusi fungsi pemotongan dengan fungsi pembulatan kurang memadai. Itu sebabnya saya meminta Anda untuk menjelaskan kepada pembaca bahwa fitur kustom hanya terpotong. Dan fungsi truncate / custom yang disebutkan oleh Anda dapat disederhanakan lebih lanjut. val doubleParts = double. toString.split (".") Sekarang dapatkan dua karakter pertama dari doubleParts.taildan concat dengan string "." dan doubleParts. headdan parse menjadi double.
Ravinder Payal
1
diperbarui, terlihat lebih baik? juga saran toString.split(".")dan doubleParts.head/tailsaran Anda mungkin mengalami alokasi array tambahan ditambah penggabungan string. perlu menguji untuk memastikannya.
cevaris
3
Baru-baru ini, saya menghadapi masalah serupa dan saya menyelesaikannya dengan menggunakan pendekatan berikut
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.
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=newBigDecimal(newBigDec(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:
Jawaban:
Anda dapat menggunakan
scala.math.BigDecimal
:Ada sejumlah mode pembulatan lainnya , yang sayangnya tidak terdokumentasi dengan baik saat ini (meskipun padanan Java mereka ).
sumber
"%.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
tapiscala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.Inilah solusi lain tanpa BigDecimals
Memotong:
Bulat:
Atau untuk p ganda dan presisi:
Hal serupa dapat dilakukan untuk fungsi pembulatan, kali ini menggunakan kari:
mana yang lebih dapat digunakan kembali, misalnya ketika pembulatan jumlah uang berikut ini dapat digunakan:
sumber
NaN
, bukan?floor
adalah yangtruncateAt(1.23456789, 8)
akan kembali1.23456788
sementararoundAt(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:sumber
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Bagaimana tentang :
sumber
((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: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.sumber
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:
sumber
Bagi yang tertarik, berikut adalah beberapa solusi yang disarankan ...
Pemotongan adalah yang tercepat, diikuti oleh BigDecimal. Perlu diingat pengujian ini dilakukan dengan menjalankan norma scala execution, tidak menggunakan alat benchmarking apapun.
sumber
...truncate or round a Double
.doubleParts.tail
dan concat dengan string "." dandoubleParts. head
dan parse menjadi double.toString.split(".")
dandoubleParts.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
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.
sumber
Sebenarnya sangat mudah untuk menangani menggunakan
f
interpolator Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.htmlMisalkan kita ingin membulatkan hingga 2 tempat desimal:
sumber
Saya tidak akan menggunakan BigDecimal jika Anda peduli dengan kinerja. BigDecimal mengonversi angka menjadi string dan kemudian menguraikannya kembali:
Saya akan tetap berpegang pada manipulasi matematika seperti yang disarankan Kaito .
sumber
Agak aneh tapi bagus. Saya menggunakan String dan bukan BigDecimal
sumber
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:
sumber