มีฟังก์ชันที่สามารถตัดหรือปัดเศษ Double ได้หรือไม่? จนถึงจุดหนึ่งในรหัสของฉันฉันต้องการให้ตัวเลขเช่น: 1.23456789
ถูกปัดเศษเป็น1.23
มีฟังก์ชันที่สามารถตัดหรือปัดเศษ Double ได้หรือไม่? จนถึงจุดหนึ่งในรหัสของฉันฉันต้องการให้ตัวเลขเช่น: 1.23456789
ถูกปัดเศษเป็น1.23
คำตอบ:
คุณสามารถใช้scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
มีโหมดการปัดเศษอื่น ๆ อีกมากมายซึ่งน่าเสียดายที่ในปัจจุบันยังไม่ได้รับการบันทึกไว้เป็นอย่างดี (แม้ว่าจะเทียบเท่ากับ Javaก็ตาม)
"%.2f".format(x).toDouble
ในกรณีนั้น ช้าลงเพียง 2 เท่าและคุณต้องใช้ไลบรารีที่คุณรู้จักอยู่แล้วเท่านั้น
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
แต่scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.
นี่เป็นอีกวิธีหนึ่งที่ไม่มี BigDecimals
ตัด:
(math floor 1.23456789 * 100) / 100
รอบ:
(math rint 1.23456789 * 100) / 100
หรือสำหรับ n คู่และความแม่นยำ p:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
สามารถทำได้เช่นเดียวกันสำหรับฟังก์ชันการปัดเศษคราวนี้ใช้การแกง:
def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
ซึ่งสามารถนำกลับมาใช้ใหม่ได้มากขึ้นเช่นเมื่อปัดเศษจำนวนเงินสามารถใช้สิ่งต่อไปนี้:
def roundAt2(n: Double) = roundAt(2)(n)
NaN
ใช่หรือไม่
floor
คือtruncateAt(1.23456789, 8)
จะกลับมา1.23456788
ในขณะที่roundAt(1.23456789, 8)
จะส่งคืนค่าที่ถูกต้องของ1.23456789
เนื่องจากยังไม่มีใครพูดถึงตัว%
ดำเนินการจึงมาที่นี่ เป็นการตัดทอนเท่านั้นและคุณไม่สามารถพึ่งพาค่าที่ส่งคืนได้ไม่ให้มีความไม่ถูกต้องของทศนิยม แต่บางครั้งก็มีประโยชน์:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
เกี่ยวกับ :
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
ไม่ได้ทดสอบมากเกินไป รหัสนี้จะทำทศนิยม 2 ตำแหน่ง
แก้ไข: แก้ไขปัญหาที่ @ryryguy ชี้ให้เห็น (ขอขอบคุณ!)
ถ้าคุณต้องการให้มันเร็ว Kaito มีความคิดที่ถูกต้อง math.pow
ช้าแม้ว่า สำหรับการใช้งานมาตรฐานใด ๆ คุณควรใช้ฟังก์ชันแบบเรียกซ้ำ:
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
}
}
จะเร็วขึ้นประมาณ 10 เท่าหากคุณอยู่ในช่วงLong
(เช่น 18 หลัก) ดังนั้นคุณสามารถปัดเศษตรงจุดใดก็ได้ระหว่าง 10 ^ 18 ถึง 10 ^ -18
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
res12: Double = 0.023514999999999998
หารด้วยนัยสำคัญแทน:math.round(x*100000)/100000.0
p10
ฟังก์ชันเรียกซ้ำด้วยการค้นหาอาร์เรย์: อาร์เรย์จะเพิ่มการใช้หน่วยความจำประมาณ 200 ไบต์ แต่น่าจะประหยัดการทำซ้ำหลายครั้งต่อการโทร
คุณสามารถใช้ชั้นเรียนโดยนัย:
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))
}
สำหรับผู้ที่สนใจนี่คือบางครั้งสำหรับแนวทางแก้ไขที่แนะนำ ...
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
การตัดทอนจะเร็วที่สุดตามด้วย BigDecimal โปรดทราบว่าการทดสอบเหล่านี้เสร็จสิ้นแล้วโดยใช้การรัน norma scala โดยไม่ได้ใช้เครื่องมือเปรียบเทียบใด ๆ
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
และต่อด้วยสตริง "." และdoubleParts. head
แยกวิเคราะห์เป็นสองเท่า
toString.split(".")
และข้อเสนอแนะของคุณdoubleParts.head/tail
อาจได้รับผลกระทบจากการจัดสรรอาร์เรย์เพิ่มเติมบวกการต่อสายอักขระ จะต้องทดสอบให้แน่ใจว่า
เมื่อเร็ว ๆ นี้ฉันประสบปัญหาที่คล้ายกันและฉันแก้ไขโดยใช้แนวทางต่อไปนี้
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)
ฉันใช้นี้ปัญหา SO ฉันมีฟังก์ชั่นที่มากเกินไปสำหรับทั้ง Float \ Double และตัวเลือกโดยนัย \ Explicit โปรดทราบว่าคุณต้องระบุประเภทการส่งคืนอย่างชัดเจนในกรณีที่มีฟังก์ชันมากเกินไป
มันง่ายมากที่จะจัดการโดยใช้ Scala f
interpolator - https://docs.scala-lang.org/overviews/core/string-interpolation.html
สมมติว่าเราต้องการปัดเศษจนถึงทศนิยม 2 ตำแหน่ง:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
ฉันจะไม่ใช้ BigDecimal ถ้าคุณสนใจเรื่องประสิทธิภาพ BigDecimal แปลงตัวเลขเป็นสตริงแล้วแยกวิเคราะห์อีกครั้ง:
/** 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)
ฉันจะยึดติดกับการคำนวณทางคณิตศาสตร์ตามที่ไคโตะแนะนำ
แปลกนิดหน่อย แต่ก็ดี ฉันใช้ String ไม่ใช่ BigDecimal
def round(x: Double)(p: Int): Double = {
var A = x.toString().split('.')
(A(0) + "." + A(1).substring(0, if (p > A(1).length()) A(1).length() else p)).toDouble
}
คุณสามารถทำได้: Math.round(<double precision value> * 100.0) / 100.0
แต่ Math.round เร็วที่สุด แต่แบ่งออกไม่ดีในกรณีมุมที่มีจำนวนตำแหน่งทศนิยมสูงมาก (เช่น round (1000.0d, 17)) หรือส่วนจำนวนเต็มขนาดใหญ่ (เช่น round (90080070060.1d, 9) )
ใช้ Bigdecimal มันไม่มีประสิทธิภาพเล็กน้อยเนื่องจากจะแปลงค่าเป็นสตริง แต่มีความสัมพันธ์กันมากขึ้น:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
ใช้การตั้งค่าโหมดการปัดเศษของคุณ
หากคุณสงสัยและต้องการทราบรายละเอียดเพิ่มเติมว่าเหตุใดจึงเกิดขึ้นคุณสามารถอ่านสิ่งนี้: