143

Is there a function that can truncate or round a Double? At one point in my code I would like a number like: 1.23456789 to be rounded to 1.23

elm
  • 20,117
  • 14
  • 67
  • 113
richsoni
  • 4,188
  • 8
  • 34
  • 47

15 Answers15

184

You can use scala.math.BigDecimal:

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

There are a number of other rounding modes, which unfortunately aren't very well documented at present (although their Java equivalents are).

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 1
    Note that this is a very expensive operation--100x slower than mult/round/mult all with floats. It's not even more compact. – Rex Kerr Jun 19 '12 at 19:23
  • @Rex: How likely are you to be rounding in performance-critical code? The standard library approach is safer, more flexible, and expresses the intent more clearly to anyone reading the code in the future. – Travis Brown Jun 19 '12 at 20:26
  • 5
    Fairly likely, I'd say. Anything involving grids or finance can require rounding and also performance. – Rex Kerr Jun 19 '12 at 20:33
  • @Rex: Sure, but unless the poster has evidence that this is a bottleneck or knows it will be called millions of times, he or she should be using the standard library. – Travis Brown Jun 19 '12 at 20:43
  • 31
    I suppose there are people for whom a long call to a clunky library is more comprehensible than simple mathematics. I'd recommend `"%.2f".format(x).toDouble` in that case. Only 2x slower, and you only have to use a library that you already know. – Rex Kerr Jun 19 '12 at 20:52
  • 7
    @RexKerr, you are not rounding in this case.. simply truncating. – José Leal Sep 27 '12 at 06:59
  • 19
    @JoséLeal - Huh? `scala> "%.2f".format(0.714999999999).toDouble` `res13: Double = 0.71` but `scala> "%.2f".format(0.715).toDouble` `res14: Double = 0.72`. – Rex Kerr Apr 20 '13 at 18:37
  • 5
    @RexKerr I prefer your string.format way, but in locales s.a. mine (Finnish), care must be taken to fix to ROOT locale. E.g. "%.2f".formatLocal(java.util.Locale.ROOT,x).toDouble . It seems, format uses ',' because of the locale whereas toDouble is not able to take it in and throws a NumberFormatException. This of course is based on where your code is being *run*, not where it's developed. – akauppi Sep 19 '14 at 10:02
  • @RexKerr I'd say your suggested answer should more or less always be avoided. And in cases when you're trying to round all floating point number in a large storage container (like a breeze DenseMatrix, say), 2x overhead is killer. For many people, that is an every-day kind of problem, and a single, simple standard library solution ought to exist which would cover dense numerical calculations as much as any other use case for rounding numbers. It's harmful to propagate a hack like string formatting for this. – ely Aug 09 '18 at 21:21
  • @ely - But you don't have anything better, do you? `BigDecimal` is far, far worse in terms of overhead. – Rex Kerr Sep 06 '18 at 17:41
  • @RexKerr I would say to use one of the `cFor` macros from `spire` in combination with a lower-level library, probably using an FFI to C code. This will remove nearly all overhead period, include loop overhead unrelated to the actual rounding. If you model it after what scala breeze does, writing this as a one-off helper function would be easy and worth it. – ely Sep 06 '18 at 21:50
  • @ely - How does `cFor` help--what are you looping over? Which C code would you use? Why leave it in C instead of converting it to Scala? – Rex Kerr Sep 13 '18 at 22:03
  • @RexKerr I already mentioned what I am looping over in my original comment above, that is what spire's cFor helps with. Leaving it in C means it will be much faster because by using cFor, you can avoid the particular function call overhead incurred in the JVM, and especially for Scala which has additional looping overhead even beyond java (and will be huge for your string formatting approach). Any C code for rounding of the appropriate type would be fine, even writing it yourself. This is similar in spirit to using Cython to speed up Python code. – ely Sep 14 '18 at 12:34
  • @ely - What is the timestamp of your comment that specifies the operation that you want to perform in a loop? I can't find it. What is "the appropriate type" of rounding? Also, you seem to be under a variety of misconceptions about performance of primitive operations on the JVM. The JVM performs inlining, loop unrolling, and various other operations, just like a good optimizing compiler does. Also, you can use either tail-recursive functions or while loops to get loops with the same bytecode as Java would produce. – Rex Kerr Sep 14 '18 at 23:10
  • @RexKerr The comment is directly above in this chain of comments, but I will [link it here](https://stackoverflow.com/questions/11106886/scala-doubles-and-precision/11107005?noredirect=1#comment91374617_11107005) as well. As the comment explains, the context in question is, for example, a very large Breeze DenseMatrix. Your comment about the JVM seems to be missing the point. [This link](https://www.chrisstucchio.com/blog/2014/learning_spire_cfor.html) may help narrow down to what I'm talking about. – ely Sep 17 '18 at 12:42
  • Note that as the post suggests, for simple loops, `cfor` can be equivalent to a while loop and the speed benefit is just from avoiding any Scala "idiomatic loop" constructs. This is not true for numerics-heavy work though, like mapping BLAS linear algebra operations via netlib-java over a dense contiguous C array, where you can actually specialize the loop with `cFor` (and others) to completely avoid Scala and JVM overhead. – ely Sep 17 '18 at 12:49
  • @ely - You still have said nothing about the actual algorithm to compute the desired number of digits. "Use cfor" isn't an algorithm. "Call C" isn't either. Specify an algorithm, not a language or general-purpose looping construct! Also, you're mistaken about whether simple and complex loops have different behavior with `cfor`. – Rex Kerr Sep 25 '18 at 23:56
  • @RexKerr why are you asking for an algorithm for the digits part... that part has literally nothing to do with the question. The issue is avoiding call overhead of Scala. You could write your own algorithm in C to repeatedly divide by 10 and check overflow / underflow conditions based on the int precision, or use library functions to do it. The algorithm has absolutely nothing to do with what matters for the question. – ely Sep 26 '18 at 12:43
  • @ely - There is no "call overhead of Scala". Scala compiles to JVM bytecode, and the JVM can inline calls if it deems it to be a good optimization. There is a **huge** call overhead for FFI code, though, because the JVM has to preventatively save the stack frame (who knows what's going on out there!). Your concerns are exactly backwards. – Rex Kerr Sep 27 '18 at 14:39
  • @RexKerr No, there absolutely is call overhead in Scala. You're forgetting that things like naive looping syntax will unpack into different types of iteration behavior in Scala. Scala compiling to JVM bytecode is not at all relevant. You're thinking of this wrong. The overhead comes from the fact that Scala *does more stuff* to support e.g. monadic extractor patterns (like a loop over an `Option`), so that naive code unpacks to much more complicated machinery under the hood, and then that big pile of stuff goes to bytecode. [See e.g. this](https://issues.scala-lang.org/browse/SI-1338) – ely Sep 27 '18 at 14:45
  • For highly optimized FFI routines, the problem is different. It is that even the optimized JVM bytecode version is far too slow, and that paying the overhead for a FFI call to a much faster machine code version compiled from another language is worth it. This is often true when applying a numeric operation like rounding to a continguous memory array. You can marshal the underlying data of the array very easily and pay that overhead once for the whole memory, then "vectorize" the operation across the data in C-land. For a large enough `DenseMatrix` this is often faster even for simple rounding. – ely Sep 27 '18 at 14:48
  • 2
    @ely - It is true that if you use Scala features that have a considerable runtime overhead, Scala will be slow. So, don't do that when writing performant code. Some operations are slow on the JVM (like random indexing anded with a bitmask--the JVM will do bounds checks) and for big operations that can make FFI a good idea. But that's **not the case here**. The question is about rounding **a single number**. If you want to be fast, you have got to understand the performance characteristics of your operations. (E.g. avoid `Option`, avoid repeated division, avoid FFI, etc..) – Rex Kerr Sep 27 '18 at 15:34
  • @RexKerr to follow that advice essentially means to always avoid Scala features when writing Scala, because this type of use case happens constantly, in every function, every day, for people working on numerical linear algebra, machine learning, etc. For these people, it should *always* be seen as a bad idea to use something like string formatting for this, along with all sorts of the other Scala overhead stuff we've discussed above. That's why I made the comment: Scala overall needs in-built support for operations that *always* need to avoid this overhead without sacrificing idiomatic code. – ely Sep 27 '18 at 16:08
  • It's a cautionary statement that if you are considering Scala for numerically intensive applications, be very careful, since every single thing you write throughout the whole system will have to pedantically avoid idiomatic Scala patterns and/or need to depend on FFIs for performant function calls. Seeing that the standard library for example has no built-in way to efficiently round a Double (while keeping its type as Double the whole time) is a strong example of this. Something that is easy in C, C++, Python, Haskell, FORTRAN, and a bunch of other languages, is artificially difficult here. – ely Sep 27 '18 at 16:11
  • 2
    @ely - No, you don't _always_ avoid Scala features, you avoid _slow features_ specifically when you _know the code needs to be fast_. That is, don't be dumb about how you write your code; adjust what you write depending on the requirements. Also, did you notice I supplied a high-performance version in a separate answer? The string answer is just an easy low-performance way to get the right behavior. If there was a good built-in floating-point round, that would be nice too, but not everything is built in. Finally, can you please name the C, C++, Haskell and/or FORTRAN functions? – Rex Kerr Sep 27 '18 at 16:20
  • For c/cpp [linked here](https://en.cppreference.com/w/c/numeric/math/round), for Haskell [linked here](https://stackoverflow.com/a/31952975/567620) and the same algorithm would work in FORTRAN. The point with Haskell and FORTRAN is also that even if there is not a fixed standard library function to do it, the language itself does not fundamentally prevent it from being possible to write your native function in idiomatic style to do it, like here in Java where you literally have to involve BigDecimal. Even so, I'd complain that even Haskell and FORTRAN need to do better and provide what C does. – ely Sep 27 '18 at 16:40
  • Regarding "avoid slow features" in your last comment.. the trouble is that if you're doing numerical linear algebra work in Scala, that means to avoid almost everything that regular idiomatic Scala will do automatically, and writing all the code (not just select spots) in a sort of un-Scala-y way all the time. I'm sure in random application code this is not an issue. Most code doesn't need to be that fast and you should not prematurely optimize. This is just not true when your everyday workflow is writing custom processing for huge matrices. – ely Sep 27 '18 at 16:42
  • @ely - You linked to the C/C++ version of what Scala already has (`math.rint`) and which doesn't solve the problem (floating point round to a specified precision). The Haskell code is not in a library, and performs poorly. My code is idiomatic for performant Scala. Matrix math can be faster in some other languages, but that's not the question, so I don't know why you're bringing it up except to vent. It is irrelevant to the question and to this answer to the question and to my suggestion for another answer in this vein. – Rex Kerr Sep 27 '18 at 19:57
  • @RexKerr some of those are fair points, except that your code is not performant in Scala when used on large dense data structures. I give up trying to convince you. You win the comment endurance contest! – ely Sep 27 '18 at 20:03
  • The format works correctly in this: `"%.4f".format(myDoubleNumber)` Examples: `"%.4f".format(1.99999) will return 2.0000` `"%.4f".format(1.23499) will return 1.2350` Of course the result is String so good for rendering only. – NKM May 12 '22 at 11:57
86

Here's another solution without BigDecimals

Truncate:

(math floor 1.23456789 * 100) / 100

Round (see rint):

(math rint 1.23456789 * 100) / 100

Or for any double n and precision p:

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

Similar can be done for the rounding function, this time using currying:

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

which is more reusable, e.g. when rounding money amounts the following could be used:

def roundAt2(n: Double) = roundAt(2)(n)
juanmirocks
  • 4,786
  • 5
  • 46
  • 46
Kaito
  • 1,775
  • 1
  • 10
  • 7
39

Since no-one mentioned the % operator yet, here comes. It only does truncation, and you cannot rely on the return value not to have floating point inaccuracies, but sometimes it's handy:

scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
akauppi
  • 17,018
  • 15
  • 95
  • 120
  • 2
    Wouldn't recommend this though it's my own answer: the same inaccuracy issues as mentioned by @ryryguy in another answer's comment affect here as well. Use string.format with the Java ROOT locale (I'll comment about that there). – akauppi Sep 19 '14 at 09:59
  • this is perfect if you just need to render the value and never use it in subsequent operations. thanks – Alexander Arendar Dec 03 '14 at 21:20
  • 3
    here is something funny: `26.257391515826225 - 0.057391515826223094 = 26.200000000000003` – kubudi Jul 09 '15 at 14:37
15

How about :

 val value = 1.4142135623730951

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

//4 decimal places
println((value * 10000).round / 10000.toDouble)
blue-sky
  • 51,962
  • 152
  • 427
  • 752
  • pretty clean solution. Here is mine for truncation: `((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDouble` didn't test it too much though. This code would do 2 decimal places. – robert Mar 30 '16 at 07:45
  • This solution works but if I need zeros in the the decimal places e.g. to keep 4 decimal places it won't. But the format works correctly in this: `"%.4f".format(myDoubleNumber)` Examples: `"%.4f".format(1.99999) will return 2.0000` `"%.4f".format(1.23499) will return 1.2350` Of course the result is String so good for rendering only. – NKM May 12 '22 at 11:54
8

It's actually very easy to handle using Scala f interpolator - https://docs.scala-lang.org/overviews/core/string-interpolation.html

Suppose we want to round till 2 decimal places:

scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198

scala> println(f"$sum%1.2f")
1.57
R Sun
  • 1,353
  • 14
  • 17
7

Edit: fixed the problem that @ryryguy pointed out. (Thanks!)

If you want it to be fast, Kaito has the right idea. math.pow is slow, though. For any standard use you're better off with a recursive function:

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

This is about 10x faster if you're within the range of Long (i.e 18 digits), so you can round at anywhere between 10^18 and 10^-18.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • 3
    Watch out, multiplying by the reciprocal doesn't work reliably, because it may not be reliably representable as a double: `scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)` ==> `res12: Double = 0.023514999999999998`. Divide by the significance instead: `math.round(x*100000)/100000.0` – ryryguy Apr 19 '13 at 22:37
  • It may also be useful to replace the recursive `p10` function with an array lookup: the array will increase memory consumption by about 200 bytes but likely save several iterations per call. – Levi Ramsey Dec 31 '18 at 19:44
5

For those how are interested, here are some times for the suggested solutions...

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

Truncation
Scala custom Formatter: Elapsed Time: 3 

Truncation is the fastest, followed by BigDecimal. Keep in mind these test were done running norma scala execution, not using any benchmarking tools.

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

}
cevaris
  • 5,671
  • 2
  • 49
  • 34
  • 1
    Dear scalaCustom is not rounding off, it's just truncating – Ravinder Payal Jan 05 '18 at 13:37
  • hmm, OP was not specific to rounding or truncating; `...truncate or round a Double`. – cevaris Jan 08 '18 at 18:19
  • But in my opinion comparing the speed / execution time of truncating function of with rounding functions is inadequate. That's why I asked you to clarify it to the reader that the custom feature only truncate. And truncate / custom function mentioned by you can be simplified further. val doubleParts = double. toString.split(".") Now get the first two chars of `doubleParts.tail` and concat with strings "." and `doubleParts. head` and parse to double. – Ravinder Payal Jan 09 '18 at 03:56
  • 1
    updated, look better? also your suggestion `toString.split(".")` and `doubleParts.head/tail` suggestion may suffer from extra array allocation plus string concatenation. would need to test to be sure though. – cevaris Jan 10 '18 at 21:27
  • @OldGaurd01 Your idea of "simplification" on a truncate/rounding function FOR NUMBERS, is to use a `String`??? In what universe is that a simplification?? At least 2 extra casts from Double -> String and String -> Double (potentially 2x for each part...). – Randomness Slayer Feb 27 '21 at 13:39
  • How can you talk about speed / execution time being "inadequate" while your suggestion recommends literally casting the precise number to a string approximation (take any repeating/irrational fraction)? Not only is it potentially less precise, you introduce rounding/truncation from the start. So not only is your suggestion in your comment slower than other suggestions, it is also less precise. @OldGaurd01 – Randomness Slayer Feb 27 '21 at 13:44
4

You may use implicit classes:

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))
}
Mitrakov Artem
  • 1,355
  • 2
  • 14
  • 22
3

Recently, I faced similar problem and I solved it using following approach

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)

I used this SO issue. I have couple of overloaded functions for both Float\Double and implicit\explicit options. Note that, you need to explicitly mention the return type in case of overloaded functions.

Community
  • 1
  • 1
Khalid Saifullah
  • 747
  • 2
  • 8
  • 21
3

Those are great answers in this thread. In order to better show the difference, here is just an example. The reason I put it here b/c during my work the numbers are required to be NOT half-up :

    import org.apache.spark.sql.types._
    val values = List(1.2345,2.9998,3.4567,4.0099,5.1231)
    val df = values.toDF
    df.show()
    +------+
    | value|
    +------+
    |1.2345|
    |2.9998|
    |3.4567|
    |4.0099|
    |5.1231|
    +------+

    val df2 = df.withColumn("floor_val", floor(col("value"))).
    withColumn("dec_val", col("value").cast(DecimalType(26,2))).
    withColumn("floor2", (floor(col("value") * 100.0)/100.0).cast(DecimalType(26,2)))

    df2.show()
+------+---------+-------+------+
| value|floor_val|dec_val|floor2|
+------+---------+-------+------+
|1.2345|        1|   1.23|  1.23|
|2.9998|        2|   3.00|  2.99|
|3.4567|        3|   3.46|  3.45|
|4.0099|        4|   4.01|  4.00|
|5.1231|        5|   5.12|  5.12|
+------+---------+-------+------+

floor function floors to the largest interger less than current value. DecimalType by default will enable HALF_UP mode, not just cut to precision you want. If you want to cut to a certain precision without using HALF_UP mode, you can use above solution instead ( or use scala.math.BigDecimal (where you have to explicitly define rounding modes).

helloworld
  • 613
  • 8
  • 24
2

Since the question specified rounding for doubles specifically, this seems way simpler than dealing with big integer or excessive string or numerical operations.

"%.2f".format(0.714999999999).toDouble
gutscdav000
  • 359
  • 1
  • 3
  • 14
1

I wouldn't use BigDecimal if you care about performance. BigDecimal converts numbers to string and then parses it back again:

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

I'm going to stick to math manipulations as Kaito suggested.

Community
  • 1
  • 1
bigonazzi
  • 714
  • 7
  • 6
0

A bit strange but nice. I use String and not 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

You can do:Math.round(<double precision value> * 100.0) / 100.0 But Math.round is fastest but it breaks down badly in corner cases with either a very high number of decimal places (e.g. round(1000.0d, 17)) or large integer part (e.g. round(90080070060.1d, 9)).

Use Bigdecimal it is bit inefficient as it converts the values to string but more relieval: BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue() use your preference of Rounding mode.

If you are curious and want to know more detail why this happens you can read this: enter image description here

frostcs
  • 353
  • 5
  • 10
0

I think previous answers are:

  • Plain wrong: using math.floor for example doesn't work for negative values..
  • Unnecessary complicated.

Here is a suggestion based on @kaito's answer (i can't comment yet):

def truncateAt(x: Double, p: Int): Double = {
    val s = math.pow(10, p)
    (x * s).toInt / s
}

toInt will work for positive and negative values.

fbetteo
  • 1
  • 1