Reputation: 15
I wanna to call exp on BigDecimal,while it seems that BigDecimal does not support this function directly.How can I achieve this goal?
Upvotes: 0
Views: 340
Reputation: 40510
Something like this maybe:
def etox(x: BigDecimal, p: Int) =
Stream.from(1).takeWhile(_ <= p)
.foldLeft((BigDecimal(1), BigDecimal(1)) -> BigDecimal(1)) {
case (((fac, pow), res), n) =>
val f = fac * n
val p = pow * x
((f, p), res + p/f)
}._2
It sums up the first p
members of the Taylor series for e^x. Taylor series converges pretty quickly, so this should give you a fairly good precision even with relatively small p
values. etox(1, 20)
for example, gets 18 digits right (in about 100 microseconds), which is already better, than Math.E
(new BigDecimal(Math.E).pow(1)
takes ~70 micros).
You can control the precision vs. speed by adjusting the p
.
On my laptop etox(1938435340345L, 1000)
takes about 50 milliseconds, while etox(1938435340345L, 10000)
took 17.5 seconds.
Note, that the higher values of x
are, the greater p
you need to get an adequate precision. For x=1
for example, p=20
is enough, as I mentioned earlier, but if x=25
, then you need p
to be at least 100
to beat the .pow
precision.
I am wondering, why this is not linear. Must have to do with the work involved to calculate those huge factorials, and gc'ing memory it takes to represent them ...
As a compromise for higher x
values, you can do something like:
etox(1, 100).pow(25)
. This gives you better precision than etox(25, 100)
, in about the same time.
BigDecimal(Math.E).pow(100)
takes 64 microseconds, and computes the first 14 digits accurately. etox(1, 1000).pow(100)
takes 1.4 milliseconds, and is accurate to the 33rd digit.
Update actually, etox(100, 1000)
gets 34 digits correctly in 1.1 millis ... so, I guess, it doesn't matter all that much which form to use. I would expect the difference to be more significant for larger x
though.
For example etox(1000, 1000)
is way off, while etox(1, 1000).pow(1000)
is still pretty good.
Upvotes: 0
Reputation: 3183
math.exp(myDecimal.toDouble)
with some loss of precision due to double encoding.
Upvotes: 1