Scala double, et la précision

y a-t-il une fonction qui peut tronquer ou arrondir un Double? À un moment dans mon code je voudrais un nombre comme: 1.23456789 à arrondir à 1.23

86
demandé sur elm 2012-06-19 22:14:55

10 réponses

vous pouvez utiliser scala.math.BigDecimal :

BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble

il existe un certain nombre d'autres modes d'arrondi , qui ne sont malheureusement pas très bien documentés à l'heure actuelle (bien que leurs équivalents Java sont ).

108
répondu Travis Brown 2012-06-19 18:22:27

Voici une autre solution sans grandes décisions

tronqué:

(math floor 1.23456789 * 100) / 100

Ronde:

(math rint 1.23456789 * 100) / 100

ou pour tout Double n et précision p:

def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }

Similaire peut être fait pour l'arrondi de la fonction, cette fois à l'aide de nourrissage:

def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }

, qui est plus réutilisable, par exemple, lorsqu'on arrondit des montants en argent, on peut utiliser ce qui suit:

def roundAt2(p: Int) = roundAt(2)(p)
65
répondu Kaito 2013-09-26 10:41:43

puisque personne n'a encore mentionné l'opérateur % , voici. Il ne fait que la troncature, et vous ne pouvez pas compter sur la valeur de retour pour ne pas avoir des inexactitudes de virgule flottante, mais parfois il est pratique:

scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
28
répondu akauppi 2014-12-04 12:48:51

Que Diriez-vous de:

 val value = 1.4142135623730951

//3 decimal places
println((value * 1000).round / 1000.toDouble)

//4 decimal places
println((value * 10000).round / 10000.toDouble)
8
répondu blue-sky 2014-06-18 20:17:26

Edit: correction du problème signalé par @ryryguy. (Merci!)

Si vous voulez qu'il soit rapide, Kaito a la bonne idée. math.pow est lent, cependant. Pour n'importe quelle utilisation standard vous êtes mieux avec une fonction récursive:

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
  }
}

c'est environ 10x plus rapide si vous êtes dans la gamme de Long (I. e 18 chiffres), de sorte que vous pouvez tourner à n'importe où entre 10^18 et 10^-18.

7
répondu Rex Kerr 2013-04-20 18:51:18

vous pouvez utiliser les classes implicites:

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))
}
4
répondu Mitrakov Artem 2014-06-20 09:39:14

récemment, j'ai fait face à un problème similaire et je l'ai résolu en utilisant l'approche suivante

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)

j'ai utilisé ce DONC problème. J'ai quelques fonctions surchargées pour les options Float\Double et implicite\explicit. Notez que vous devez mentionner explicitement le type de retour en cas de surcharge de fonctions.

3
répondu Khalid Saifullah 2017-05-23 12:10:40

pour ceux qui sont intéressés, voici quelques fois pour les solutions suggérées...

Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27

Truncation
Scala custom Formatter: Elapsed Time: 3 
La troncature

est la plus rapide, suivie de BigDecimal. Gardez à l'esprit que ces tests ont été réalisés en exécutant l'exécution de norma scala, sans utiliser d'outils de benchmarking.

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())
      }
    }
  }

}
3
répondu cevaris 2018-01-10 21:25:08

Je n'utiliserais pas BigDecimal si vous vous souciez de performance. BigDecimal convertit les nombres en chaîne et les parse à nouveau:

  /** 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)

je vais m'en tenir aux manipulations mathématiques comme le suggère Kaito .

1
répondu bigonazzi 2017-05-23 12:26:26

un peu étrange mais sympa. J'ai utiliser des chaînes de caractères et non pas 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
}
0
répondu jafed 2017-07-09 16:52:14