Bir Çifti kesebilen veya yuvarlayabilen bir işlev var mı? Kodumun bir noktasında şunun gibi bir sayı istiyorum: 1.23456789
yuvarlanacak1.23
Bir Çifti kesebilen veya yuvarlayabilen bir işlev var mı? Kodumun bir noktasında şunun gibi bir sayı istiyorum: 1.23456789
yuvarlanacak1.23
Yanıtlar:
Şunları kullanabilirsiniz scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
Maalesef şu anda çok iyi belgelenmemiş ( Java eşdeğerleri olsa da ) bir dizi başka yuvarlama modu vardır .
"%.2f".format(x).toDouble
Bu durumda tavsiye ederim . Yalnızca 2 kat daha yavaştır ve yalnızca zaten bildiğiniz bir kitaplığı kullanmanız gerekir.
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
ama scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.
İşte BigDecimals olmadan başka bir çözüm
kırp:
(math floor 1.23456789 * 100) / 100
Yuvarlak:
(math rint 1.23456789 * 100) / 100
Veya herhangi bir çift n ve hassas p için:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
Yuvarlama işlevi için de benzer bir işlem yapılabilir, bu sefer körleme kullanılarak:
def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
bu daha çok tekrar kullanılabilir, örneğin para tutarlarını yuvarlarken aşağıdakiler kullanılabilir:
def roundAt2(n: Double) = roundAt(2)(n)
NaN
, değil mi?
floor
DİR truncateAt(1.23456789, 8)
dönecektir 1.23456788
ise roundAt(1.23456789, 8)
doğru değeri döndürecektir1.23456789
%
Henüz kimse operatörden bahsetmediğine göre , işte geliyor. Yalnızca kısaltma yapar ve kayan nokta yanlışlıklarının olmaması için dönüş değerine güvenemezsiniz, ancak bazen kullanışlıdır:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Peki ya:
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
yine de çok fazla test etmedim. Bu kod 2 ondalık basamak yapar.
Düzenleme: @ryryguy'un işaret ettiği sorunu çözdü. (Teşekkürler!)
Hızlı olmasını istiyorsanız, Kaito'nun doğru fikri var. math.pow
yavaş olsa da. Herhangi bir standart kullanım için özyinelemeli bir işlevle daha iyi durumda olursunuz:
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
}
}
Bu, Long
(yani 18 basamaklı) aralığındaysanız yaklaşık 10 kat daha hızlıdır , böylece 10 ^ 18 ile 10 ^ -18 arasında herhangi bir yerde yuvarlayabilirsiniz.
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==> res12: Double = 0.023514999999999998
. Bunun yerine math.round(x*100000)/100000.0
p10
işlevi bir dizi aramasıyla değiştirmek de yararlı olabilir : dizi bellek tüketimini yaklaşık 200 bayt artıracak, ancak muhtemelen çağrı başına birkaç yineleme kaydedecektir.
Örtük sınıfları kullanabilirsiniz:
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))
}
İlgilenenler için, işte önerilen çözümler için bazı zamanlar ...
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
Kesme en hızlı olanıdır ve onu BigDecimal izler. Unutmayın, bu testler herhangi bir kıyaslama aracı kullanılmadan norma scala çalıştırılarak yapıldı.
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
ve dizelerle birlikte "." ve doubleParts. head
ve ikiye bölün.
toString.split(".")
ve doubleParts.head/tail
öneriniz fazladan dizi tahsisi artı dize birleştirme sorunlarından da etkilenebilir. emin olmak için test etmeniz gerekir.
Son zamanlarda benzer bir sorunla karşılaştım ve aşağıdaki yaklaşımı kullanarak çözdüm
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)
Kullandığım bu SO sorunu. Float \ Double ve örtük \ açık seçenekler için birkaç aşırı yüklenmiş işlevim var. Unutmayın, aşırı yüklenmiş işlevler durumunda dönüş türünü açıkça belirtmeniz gerekir.
Scala f
interpolator kullanarak işlemek aslında çok kolay - https://docs.scala-lang.org/overviews/core/string-interpolation.html
2 ondalık basamağa kadar yuvarlamak istediğimizi varsayalım:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
Performansı önemsiyorsanız BigDecimal'ı kullanmam. BigDecimal, sayıları dizeye dönüştürür ve ardından tekrar ayrıştırır:
/** 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)
Kaito'nun önerdiği gibi matematik manipülasyonlarına bağlı kalacağım .
Şunları yapabilirsiniz: Math.round(<double precision value> * 100.0) / 100.0
Ancak Math.round en hızlısıdır ancak çok yüksek sayıda ondalık basamaklı (ör. Yuvarlak (1000.0d, 17)) veya büyük tam sayı bölümü (ör. Yuvarlak (90080070060.1d, 9) olan köşe durumlarda kötü bir şekilde bozulur. ).
Bigdecimal kullanın, değerleri dizeye dönüştürdüğü için biraz verimsizdir, ancak daha güvenilirdir:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
Tercihinizi Yuvarlama modunu kullanın.
Merak ediyorsanız ve bunun neden olduğunu daha fazla bilmek istiyorsanız, şunu okuyabilirsiniz: