Reputation: 5618
I want to switch from Java to a scripting language for the Math based modules in my app. This is due to the readability, and functional limitations of mathy Java.
For e.g, in Java I have this:
BigDecimal x = new BigDecimal("1.1");
BigDecimal y = new BigDecimal("1.1");
BigDecimal z = x.multiply(y.exp(new BigDecimal("2"));
As you can see, without BigDecimal operator overloading, simple formulas get complicated real quick.
With doubles, this looks fine, but I need the precision.
I was hoping in Scala I could do this:
var x = 1.1;
var y = 0.1;
print(x + y);
And by default I would get decimal-like behaviour, alas Scala doesn't use decimal calculation by default.
Then I do this in Scala:
var x = BigDecimal(1.1);
var y = BigDecimal(0.1);
println(x + y);
And I still get an imprecise result.
Is there something I am not doing right in Scala?
Maybe I should use Groovy to maximise readability (it uses decimals by default)?
Upvotes: 31
Views: 36647
Reputation: 24443
I know this question is old and answered, but another option, if you're open to different languages (as the OP seemed to be), would be to use Clojure. Clojure has, IMO, some of the simplest syntax for BigDecimal
math (note the trailing M
s -- that indicates BigDecimal
):
user=> (def x 1.1M)
#'user/x
user=> (def y 1.1M)
#'user/y
user=> (def z (* x (.pow y 2)))
#'user/z
user=> z
1.331M
user=> (type z)
java.math.BigDecimal
I like Clojure for math since it defaults to precision in many cases, e.g. its use of Ratio
:
user=> (/ 60 14)
30/7
user=> (type (/ 60 14))
clojure.lang.Ratio
Upvotes: 2
Reputation: 13221
You can store values as Integer/String (without precision) internally and use scale
(this is a transcript from Scala REPL):
scala> val Scale = 2
Scale: Int = 2
scala> val x = BigDecimal(110, Scale)
x: scala.math.BigDecimal = 1.10
scala> val y = BigDecimal(303, Scale)
y: scala.math.BigDecimal = 3.03
scala> (x+y, (x+y).scale)
res0: (scala.math.BigDecimal, Int) = (4.13,2)
scala> (x*2, (x*2).scale)
res1: (scala.math.BigDecimal, Int) = (2.20,2)
Or if you want to parse a string, you can control rounding:
scala> val z = BigDecimal("8.937").setScale(Scale, BigDecimal.RoundingMode.FLOOR)
z: scala.math.BigDecimal = 8.93
scala> val z = BigDecimal("8.937").setScale(Scale, BigDecimal.RoundingMode.CEILING)
z: scala.math.BigDecimal = 8.94
Upvotes: 7
Reputation: 49695
Scala is most definitely the same as Java in this respect.
As per Joachim's answer, writing val x = BigDecimal(1.1)
is equivalent to writing
val d : Double = 1.1
val x = BigDecimal(d)
The problem, of course, is that the Double d
ALREADY has the rounding error, so you're initialising x with bad data.
Use the constructor that accepts a string instead, and all will be fine.
Given your example, you'd also be better off using val
s instead of var
s, and you can safely leave the semicolons off in Scala as well.
Upvotes: 13
Reputation: 18859
scala> implicit def str2tbd(str: String) = new {
| def toBD = BigDecimal(str)
| }
str2tbd: (str: String)java.lang.Object{def toBD: scala.math.BigDecimal}
scala> var x = "1.1".toBD
x: scala.math.BigDecimal = 1.1
scala> var y = "0.1".toBD
y: scala.math.BigDecimal = 0.1
scala> x + y
res0: scala.math.BigDecimal = 1.2
scala> implicit def str2bd(str: String) = BigDecimal(str)
str2bd: (str: String)scala.math.BigDecimal
scala> x + y + "1.2345566"
res1: scala.math.BigDecimal = 2.4345566
scala>
Upvotes: 6
Reputation: 206776
Change your Scala code to this:
var x = BigDecimal("1.1"); // note the double quotes
var y = BigDecimal("0.1");
println(x + y);
and it will work just like it does in Java.
Upvotes: 36
Reputation: 308001
I don't know Scala, but in Java new BigDecimal(1.1)
initializes the BigDecimal
with a double
value and thus it is not exactly equal to 1.1. In Java you have to use new BigDecimal("1.1")
instead. Maybe that will help in Scala as well.
Upvotes: 40