Multiply a BigDecimal with an Integer - scala

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

Related

Multiply Double with BigDecimal

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

Scala BigDecimal - loss of precision

I need to do some precision calcs with Big Numbers and I have been trying with Scala BigDecimal but I have noted loss of precision.
As an example:
2^63 == 9223372036854775808
2^64 == 18446744073709551616
However when I do
println(BigDecimal.decimal(scala.math.pow(2, 63)).toBigIntExact())
println(BigDecimal.decimal(scala.math.pow(2, 64)).toBigIntExact())
I get
9223372036854776000 != 9223372036854775808
18446744073709552000 != 18446744073709551616
I don't know if I can get the exact BigInt.
Maybe I have to take other approach.
Could anyone help me to fix this issue?
# scala.math.pow(2, 63)
res0: Double = 9.223372036854776E18
You get Double on math.pow, and then you pass the result to BigDecimal - it means that you lost precision even before you started using Big* classes.
If you put numbers into BigDecimal when they are still small and haven't yet lost precision (and if you use the constructors correctly) then you'll get the expected result:
# BigDecimal(2).pow(63).toBigInt
res4: BigInt = 9223372036854775808
# BigDecimal(2).pow(64).toBigInt
res5: BigInt = 18446744073709551616
# BigDecimal(2).pow(63).toBigIntExact
res6: Option[BigInt] = Some(9223372036854775808)
# BigDecimal(2).pow(64).toBigIntExact
res7: Option[BigInt] = Some(18446744073709551616)

Precision set to only 1 decimal place only in scala if all zeros in precision

Scala seems to drop all the decimal places and keep just 1 if we convert an Integer to BigDecimal.
How can I tell the compile to preserve the decimal places irrespective of input.
scala> val tmp:BigDecimal = 1.00
tmp: BigDecimal = 1.0
// was expecting 1.00
scala> val tmp:BigDecimal = 1.01
tmp: BigDecimal = 1.01
scala> val tmp:BigDecimal = 1.11
tmp: BigDecimal = 1.11
EDIT 1: Some context on why I am trying to preserve the decimal places. I work in a fintech job, where the decimals upto 3 places is mandatory for all amount fields. The scala api is leveraged by Front-End team to show customer balance, reports etc.
From the ScalaDocs page we learn:
In most cases, the value of the BigDecimal is also rounded to the precision specified by the MathContext. To create a BigDecimal with a different precision than its MathContext, use new BigDecimal(new java.math.BigDecimal(...), mc).
And, indeed, that does appear to work.
import java.math.{BigDecimal=>JBD, MathContext=>JMC}
val a = BigDecimal(2.4) //a: scala.math.BigDecimal = 2.4
a.precision //res0: Int = 2
val b = BigDecimal(new JBD(2.4, new JMC(7))) //b: scala.math.BigDecimal = 2.400000
b.precision //res1: Int = 7
But, unfortunately, the results aren't always consistent.
import java.math.{BigDecimal=>JBD, MathContext=>JMC}
val a = BigDecimal(2.5) //a: scala.math.BigDecimal = 2.5
a.precision //res0: Int = 2
val b = BigDecimal(new JBD(2.5, new JMC(7))) //b: scala.math.BigDecimal = 2.5
b.precision //res1: Int = 2
So it would appear that precision is either set by the MathContext or it is whatever is sufficient to accurately represent the stated value, whichever is smaller. (Warning: supposition from limited observation.)
However, if what you need is a consistent presentation of the value then I suggest we move away from MathContext to StringContext.
val a :BigDecimal = 1
val b :BigDecimal = 2.2
val c :BigDecimal = 3.456
f"$a%.2f" //res0: String = 1.00
f"$b%.2f" //res1: String = 2.20
f"$c%.2f" //res2: String = 3.46 (note the rounding)

Losing precision when moving to Spark for big decimals

Below is the sample test code and its output. I see that java bigDecimal stores all the digits where as scala BigDecimal is losing on precision and does some rounding off and the same is happening with spark. Is there a way to set the precision or say never round off. I do not want to truncate or round off in any case
val sc = sparkSession
import java.math.BigDecimal
import sc.implicits._
val bigNum : BigDecimal = new BigDecimal(0.02498934809987987982348902384928349)
val convertedNum: scala.math.BigDecimal = scala.math.BigDecimal(bigNum)
val scalaBigNum: scala.math.BigDecimal = scala.math.BigDecimal(0.02498934809987987982348902384928349)
println("Big num in java" + bigNum)
println("Converted " + convertedNum)
println("Big num in scala " + scalaBigNum)
val ds = List(scalaBigNum).toDS()
println(ds.head)
println(ds.toDF.head)
Output
Big num in java0.0249893480998798801773208566601169877685606479644775390625
Converted 0.0249893480998798801773208566601169877685606479644775390625
Big num in scala 0.02498934809987988
0.024989348099879880
[0.024989348099879880]
Based on spark.apache.org/docs
The precision can be up to 38, scale can also be up to 38 (less or equal to precision). The default precision and scale is (10, 0).
here: https://www.scala-lang.org/api/2.12.5/scala/math/BigDecimal.html
But if you want in a simple way then how about convert it to String before
converting to DF or DS in order to get the precise value. :)
Just try if you want :)

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