Reputation: 4278
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
Upvotes: 146
Views: 181032
Reputation: 396
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 reliable:
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:
Upvotes: 1
Reputation: 1765
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)
Upvotes: 86
Reputation: 1
I think previous answers are:
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.
Upvotes: 0
Reputation: 366
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
Upvotes: 2
Reputation: 673
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).
Upvotes: 3
Reputation: 1671
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
Upvotes: 10
Reputation: 5794
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())
}
}
}
}
Upvotes: 5
Reputation: 351
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
}
Upvotes: 0
Reputation: 834
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.
Upvotes: 1
Reputation: 18046
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
Upvotes: 40
Reputation: 1513
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))
}
Upvotes: 4
Reputation: 53806
How about :
val value = 1.4142135623730951
//3 decimal places
println((value * 1000).round / 1000.toDouble)
//4 decimal places
println((value * 10000).round / 10000.toDouble)
Upvotes: 16
Reputation: 747
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.
Upvotes: 3
Reputation: 167891
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.
Upvotes: 7
Reputation: 139038
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).
Upvotes: 190