Scala удваивается, и точность
есть ли функция, которая может усечь или округлить двойной? В один момент в моем коде я хотел бы номер как: 1.23456789
округляется до 1.23
10 ответов:
можно использовать
scala.math.BigDecimal
:BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
есть ряд других режимы округления, который, к сожалению, не очень хорошо документированы в настоящее время (хотя их эквиваленты Java).
вот еще одно решение без BigDecimals
обрезать:
(math floor 1.23456789 * 100) / 100
тур:
(math rint 1.23456789 * 100) / 100
или двойной точности Н И П:
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(p: Int) = roundAt(2)(p)
Так как никто не упоминал
%
оператор еще, вот идет. Это только усечение, и вы не можете полагаться на возвращаемое значение, чтобы не иметь неточностей с плавающей запятой, но иногда это удобно:scala> 1.23456789 - (1.23456789 % 0.01) res4: Double = 1.23
Как насчет :
val value = 1.4142135623730951 //3 decimal places println((value * 1000).round / 1000.toDouble) //4 decimal places println((value * 10000).round / 10000.toDouble)
Edit: Исправлена проблема, на которую указал @ryryguy. (Спасибо!)
Если вы хотите, чтобы это было быстро, у Кайто есть правильная идея. медленно,. Для любого стандартного использования вам лучше использовать рекурсивную функцию:
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.
вы можете использовать неявные классы:
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)) }
недавно я столкнулся с подобной проблемой и я решил использовать следующий подход
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 и implicit\explicit. Обратите внимание, что в случае перегруженных функций необходимо явно указать тип возвращаемого значения.
для тех, как заинтересованы, вот несколько раз для предлагаемых решений...
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 execution, а не с использованием каких-либо инструментов бенчмаркинга.
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()) } } } }
Я бы не использовал 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)
Я собираюсь придерживаться математических манипуляций как Кайто предложил.