Có một hàm có thể cắt ngắn hoặc làm tròn một Double không? Tại một thời điểm trong mã của tôi, tôi muốn một số như: 1.23456789
được làm tròn thành1.23
Có một hàm có thể cắt ngắn hoặc làm tròn một Double không? Tại một thời điểm trong mã của tôi, tôi muốn một số như: 1.23456789
được làm tròn thành1.23
Câu trả lời:
Bạn có thể sử dụng scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
Có một số chế độ làm tròn khác , tiếc là hiện tại không được ghi chép đầy đủ (mặc dù các chế độ tương đương Java của chúng là như vậy ).
"%.2f".format(x).toDouble
trong trường hợp đó. Chỉ chậm hơn 2 lần và bạn chỉ phải sử dụng một thư viện mà bạn đã biết.
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
nhưng scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.
Đây là một giải pháp khác không có BigDecimals
Cắt ngắn:
(math floor 1.23456789 * 100) / 100
Tròn:
(math rint 1.23456789 * 100) / 100
Hoặc đối với n kép và p chính xác bất kỳ:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
Tương tự có thể được thực hiện cho chức năng làm tròn, lần này bằng cách sử dụng cà ri:
def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
cái nào có thể tái sử dụng nhiều hơn, ví dụ như khi làm tròn số tiền, có thể sử dụng những thứ sau:
def roundAt2(n: Double) = roundAt(2)(n)
NaN
, phải không?
floor
là truncateAt(1.23456789, 8)
sẽ trở lại 1.23456788
trong khi roundAt(1.23456789, 8)
sẽ trả về giá trị đúng1.23456789
Vì chưa có ai đề cập đến %
nhà điều hành, nên đến đây. Nó chỉ thực hiện việc cắt ngắn và bạn không thể dựa vào giá trị trả về để không có dấu chấm động không chính xác, nhưng đôi khi nó rất tiện dụng:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Làm thế nào về :
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
mặc dù không kiểm tra nó quá nhiều. Mã này sẽ làm 2 chữ số thập phân.
Chỉnh sửa: đã khắc phục sự cố mà @ryryguy đã chỉ ra. (Cảm ơn!)
Nếu bạn muốn nó nhanh chóng, Kaito có ý tưởng phù hợp. math.pow
là chậm, mặc dù. Đối với bất kỳ mục đích sử dụng tiêu chuẩn nào, bạn nên sử dụng hàm đệ quy:
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
}
}
Điều này nhanh hơn khoảng 10 lần nếu bạn nằm trong phạm vi Long
(tức là 18 chữ số), vì vậy bạn có thể làm tròn ở bất kỳ đâu trong khoảng từ 10 ^ 18 đến 10 ^ -18.
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==> res12: Double = 0.023514999999999998
. Chia cho tầm quan trọng thay vì:math.round(x*100000)/100000.0
p10
hàm đệ quy bằng tra cứu mảng: mảng sẽ tăng mức tiêu thụ bộ nhớ khoảng 200 byte nhưng có khả năng tiết kiệm một số lần lặp cho mỗi cuộc gọi.
Bạn có thể sử dụng các lớp ngầm định:
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))
}
Đối với những người quan tâm, đây là một số thời gian cho các giải pháp được đề xuất ...
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
Cắt ngắn là nhanh nhất, tiếp theo là BigDecimal. Hãy nhớ rằng các bài kiểm tra này được thực hiện khi chạy thực thi theo tỷ lệ chuẩn, không sử dụng bất kỳ công cụ đo điểm chuẩn nào.
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
và nối với chuỗi "." và doubleParts. head
và phân tích cú pháp thành gấp đôi.
toString.split(".")
và gợi ý của bạn doubleParts.head/tail
có thể bị phân bổ thêm mảng cộng với nối chuỗi. sẽ cần phải kiểm tra để chắc chắn.
Gần đây, tôi gặp phải vấn đề tương tự và tôi đã giải quyết nó bằng cách tiếp cận sau
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)
Tôi sử dụng này vấn đề SO. Tôi có một vài hàm quá tải cho cả tùy chọn Float \ Double và implicit \ explicit. Lưu ý rằng, bạn cần phải đề cập rõ ràng kiểu trả về trong trường hợp các hàm quá tải.
Nó thực sự rất dễ xử lý bằng cách sử dụng bộ f
nội suy Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.html
Giả sử chúng ta muốn làm tròn đến 2 chữ số thập phân:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
Tôi sẽ không sử dụng BigDecimal nếu bạn quan tâm đến hiệu suất. BigDecimal chuyển đổi số thành chuỗi và sau đó phân tích cú pháp lại nó:
/** 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)
Tôi sẽ gắn bó với các thao tác toán học như Kaito đề nghị.
Bạn có thể làm:Math.round(<double precision value> * 100.0) / 100.0
Nhưng Math.round là nhanh nhất nhưng nó bị hỏng nặng trong các trường hợp góc có số lượng vị trí thập phân rất cao (ví dụ: tròn (1000.0d, 17)) hoặc phần nguyên lớn (ví dụ: tròn (90080070060.1d, 9) ).
Sử dụng Bigdecimal nó hơi không hiệu quả vì nó chuyển đổi các giá trị thành chuỗi nhưng nhẹ hơn:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
sử dụng tùy chọn của bạn về chế độ Làm tròn.
Nếu bạn tò mò và muốn biết thêm chi tiết tại sao điều này xảy ra, bạn có thể đọc phần này: