Multiply Double with BigDecimal - scala

I have a BigDecimals that's always have 30 digits after the decimal points.
And I want to multiply them with Double and get a BigDecimal with 30 digits after decimal point.
For exemple I have:
val double = 4.0
val bd = 0.111111111111111111111111111111
def multiply(d : Double, bd : BigDecimal, scale: Int) = {
BigDecimal.valueOf(d).setScale(scale).*(bd)
}
// multiply(double,bd,30) => 0.4444444444444444000000000000000000
I expected to get 0.444444444444444444444444444444 (30 times 4 after the point)
What is wrong with my method?

Your bd isn't a BigDecimal, it's a Double:
scala> val bd = 0.111111111111111111111111111111
val bd: Double = 0.1111111111111111
It gets implicitly converted to a BigDecimal (after losing precision from Double) when you call multiply.
To create the BigDecimal, you seek, it's probably best to parse a string representation:
val bd = BigDecimal("0.111111111111111111111111111111")
// bd.scale is 30
It's also probably a good idea to explicitly set the scale again in multiply after the multiplication, as it's possible for multiplying two numbers, even with the same scale, to have greater scale than either number.
def multiply(d: Double, bd: BigDecimal, scale:Int): BigDecimal =
(BigDecimal.valueOf(d).setScale(scale) * bd).setScale(scale)

Scala's BigDecimal is similar to Java's BigDecimal.
When you multiply two BigDecimals together (using the * method on the left one), you get a new BigDecimal whose scale is the sum of the scale of its operands (inputs).
The variable bd is not a BigDecimal, so its name is misleading. It is actually a double. In order to instantiate a BigDecimal, you should use a constructor, for example: BigDecimal("0.111111111111111111111111111111").
val bd = BigDecimal("0.111111111111111111111111111111")
val double = 4.0
// There is no need to call `setScale` on `d` due to the first point noted above. Set it after multiplying:
def multiply(d : Double, bd : BigDecimal, scale: Int) =
(BigDecimal.valueOf(d) * bd).setScale(scale)
val result = multiply(double, bd, 30)
println(result)
println(result.scale)
Result:
0.444444444444444444444444444444
30

Related

Multiply a BigDecimal with an Integer

I want to multiply a financial amount with a quantity. I know Scala uses Java's BigDecimal under the hood but the syntax doesn't seem to be the same.
val price = BigDecimal("0.01") // £0.01
val qty = 10
I tried to do this
BigDecimal(price).*(BigDecimal(qty))
But it's a compile error. If I look at the Java SO posts you can pass integer into BigDecimal and then multiply it like this
BigDecimal(price).multiply(BigDecimal(qty))
So how do you do this in Scala? And are there any dangers in losing precision by multiplying a decimal and integer like this? I will need sum a lot of these together as well
You can actually multiply a BigDecimal with an Int using BigDecimal's multiplication operator:
def *(that: BigDecimal): BigDecimal
since the Int you will provide as its parameter will be implicitly converted to a BigDecimal based on:
implicit def int2bigDecimal(i: Int): BigDecimal
You can thus safely multiply your BigDecimal with an Int as if it was a BigDecimal and receive a BigDecimal with no loss in precision:
val price = BigDecimal("0.01")
// scala.math.BigDecimal = 0.01
val qty = 10
// Int = 10
price * qty // same as BigDecimal("0.01").*(10)
// scala.math.BigDecimal = 0.10
You can do this:
val a = 10
val b = BigDecimal(0.1000000000001)
a * b
res0: scala.math.BigDecimal = 1.0000000000010
As you can see you don´t lose precision
The problem is actually this:
BigDecimal(price)
price is already a BigDecimal so the compiler does't know what to do! If you fix this, the first version works. The second version fails because there is no multiply method on BigDecimal.
However, as others have pointed out, the simple solution is just
price*qty

How do I format numbers as percentages in Scala?

What is the simplest/idiomatic way to format percentages in Scala?
I have the following solution but I'm wondering if a more concise way exists:
val value = 0.1456
val s1 = f"the float value is ${value}%.2f"
val s2= s"the percent value is ${java.text.NumberFormat.getPercentInstance.format(value)}"
value: Double = 0.1456
s1: String = the float value is 0.15
s2: String = the percent value is 15%
If you are looking for more of a concise method, the following works and goes along with your initial idea in your code. It is also easy to add on decimal placement without having to resort to the implicit functionality. Obviously these needs to be used a lot a better solution is with the implicit method.
val value = 0.1456
val s2 = val s2 = f"the percent value is ${value*100}%.0f%%"
s2: String = the percent value is 15%
just to give a couple of other runs as well (t show rounding down here):
val value2 = 0.1416
val s3 = val s2 = f"the percent value is ${value2*100}%.0f%%"
s3: String = the percent value is 14%
Example to show adding decimal places:
val s4 = f"the percent value is ${value2*100}%.1f%%"
s4: String = the percent value is 14.2%
You could use the "pimp my library" pattern to add the asPercentage method to Doubles.
implicit class DoubleAsPercentage(d: Double) {
def asPercentage = java.text.NumberFormat.getPercentInstance.format(d)
}
val s2 = s"the percent value is ${value.asPercentage}"
You can use the f string to format the percentage to the relevant decimal places you wish. In this example, it has 4 decimal places however if you only want to return two decimal places, then use .2f in the string below.
To return 3 decimal places, then use .3f etc etc. However if you want more decimal places than suggested; "here they are 4", you will end up with trailing zeros.
val percentage = 51.9938
scala> f"I scored $percentage%.8f%% in my exams"
res164: String = I scored 51.99380000% in my exams
scala> f"I scored $percentage%.2f%% in my exams"
res165: String = I scored 51.99% in my exams

How to perform calculation given multiple types of numbers?

I have some numbers list that I go through it, and doing a simple calculation to for two numbers:
I have numberA and numberB, and the calculation im doing is:
val res = (numberA/numberB) * 100
now, I dont know what type is the number, but I know it could be float (with 2 nums after the dot) or integer...
so I want to know what is the syntax in scala to calculate it?
currently I have:
val num1 = (vatReclaimed.toInt/vatPaid.toInt) * 100
but its obviously dont work, and I cannot really use toInt i guess since I dont know the type...
whats important to me that the res will hold the right answer, so if its 2.3 * 4 the res will hold 9.2
thanksss!
When you do not know the type only, that is a number, you probably have to work with Numeric. With numeric you can convert any numeric value to Double, Float, Int or Long. For precision I would recommend Double:
def calc[A : Numeric, B : Numeric](a: A, b: B): Double = (a.toDouble / b.toDouble) * 100

String formatting with maximum number of digits

I'm trying to format floating point numbers with a maximum number of digits, but I don't want unnecessary trailing zeroes. I thought if I use g instead of f it would work (cf. this question)
def testF(d: Double) = f"$d%1.2f"
def testG(d: Double) = f"$d%1.2g"
Now this behaves rather strangely:
testF(3.1415) // --> 3.14 ok
testF(3.1) // --> 3.10 hmmm, don't want that zero
testG(3.1415) // --> 3.1 what the ?
Ok, so perhaps I need to increase the digits by one for g:
def testG(d: Double) = f"$d%1.3g"
testG(3.1415) // --> 3.14 ok
testG(3.1) // --> 3.10 grmpf
So two questions—one, why the heck is g dropping one digit and doesn't seem to care about trailing zeroes? Two, how can I have
testX(3.1415) // --> 3.14
testX(3.1) // --> 3.1
?
You could use the java DecimalFormat but it may not please the eye:
def testX(d: Double) = new java.text.DecimalFormat("#.##").format(d)
Also to answer your first question why the heck is g dropping one digit and doesn't seem to care about trailing zeroes
For the floating-point conversions 'e', 'E', and 'f' the precision is the number of digits after the decimal separator. If the conversion is 'g' or 'G', then the precision is the total number of digits in the resulting magnitude after rounding.
Formatter details
As with the C world's printf, Java's (and Scala's) Formatter includes both minimum and maximum field widths (for the %s format specifier):
// Minimum field width -- right-aligned
scala> "%23s".format(23)
res0: String = " 23"
// Minimum field width -- left-aligned
scala> "%-23s".format(23)
res1: String = "23 "
// Maximum field width with right truncation
scala> "%.3s".format(12345)
res2: String = 123
// Minimum and maximum width -- right aligned
scala> "%3.3s".format(1)
res3: String = " 1"
// Minimum and maximum width -- left aligned
scala> "%-3.3s".format(1)
res4: String = "1 "
How about this version?
The use of Formattable requires the %s format, but you're given the formatting args to interpret as you like.
A Formattable can pop up in the middle of an f-interpolation, since the normal format just uses either toString or your custom formatTo.
package object succinctly {
import java.util.{ Formattable, FormattableFlags, Formatter }
import scala.language.implicitConversions
implicit class Succinctly(val __thing: Double) extends AnyVal {
#inline def succinctly(s: String): String = s format fmtable(__thing)
#inline def succinctly: Formattable = fmtable(__thing)
}
private[this] val trailing = "(.*\\...*?)(0*)".r
private[this] def fmtable(a: Double) = new Formattable {
override def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int) = formatter.out append (
if (precision <= 0) a.toInt.toString
else s"%${width}.${precision}f" format a.asInstanceOf[java.lang.Double] match { case trailing(num, _) => num }
)
}
}
package succinctly {
import scala.language.postfixOps
import scala.math._
object Test extends App {
Console println (Pi succinctly "%1.2s")
Console println (3.1 succinctly "%1.2s")
Console println (3.0 succinctly "%1.2s")
Console println f"${3.1 succinctly}%1.2s"
}
}
It's also conceivable to write a custom interpolator that promotes doubles to trailless and changes %f to %s, though at some cost, since the f-interpolator is a macro.

Scala Doubles, and Precision

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
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).
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)
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
How about :
val value = 1.4142135623730951
//3 decimal places
println((value * 1000).round / 1000.toDouble)
//4 decimal places
println((value * 10000).round / 10000.toDouble)
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.
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())
}
}
}
}
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
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))
}
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.
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).
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.
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
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
}
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:
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.