Чи є функція, яка може обрізати або обводити 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
хоч не перевіряв її занадто багато. Цей код мав би два десяткових знаки.
Редагувати: виправлена проблема, на яку вказував @ryryguy. (Дякую!)
Якщо ви хочете, щоб це було швидко, Каїто має правильну ідею. 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. Майте на увазі, що ці випробування були виконані з виконанням нормального масштабування, не використовуючи жодних інструментів порівняльного аналізу.
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
та concat з рядками ". і 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)
Я використав цю проблему. У мене є кілька перевантажених функцій як для опцій Float \ Double, так і для неявних \ явних. Зауважте, що вам потрібно чітко вказати тип повернення у разі перевантаження функцій.
Насправді дуже просто впоратися за допомогою f
інтерполятора Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.html
Припустимо, ми хочемо округлити до двох знаків після коми:
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)
Я буду дотримуватися математичних маніпуляцій, як запропонував Кайто .
Ви можете зробити: Math.round(<double precision value> * 100.0) / 100.0
Але Math.round є найшвидшим, але він погано розбивається у кутових випадках з дуже великою кількістю десяткових знаків (наприклад, кругла (1000.0d, 17)) або великою цілою частиною (наприклад, кругла (90080070060.1d, 9) ).
Використовуйте Bigdecimal, це трохи неефективно, оскільки він перетворює значення в рядки, але більше полегшення:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
використовуйте свої переваги режиму Округлення.
Якщо вам цікаво і хочете дізнатися детальніше, чому це відбувається, ви можете прочитати це: