views:

429

answers:

5

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)?

+10  A: 

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.

Joachim Sauer
+10  A: 

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.

Jesper
+6  A: 
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>
Eastsun
This doesn't actually answer the question, it simply demonstrates a different way to use the string constructor as already covered in different answers!
Kevin Wright
+7  A: 

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 vals instead of vars, and you can safely leave the semicolons off in Scala as well.

Kevin Wright
+2  A: 

You can store values as Integer/String (without precision) internally and use scale (this is a transcript from Scala REPL):

scala> import java.math.{BigDecimal => JBigD}
scala> import java.math.{BigInteger => JBigI}

scala> val Scale = 2

scala> val x = new BigDecimal(new JBigD(JBigI.valueOf(110), Scale))
x: scala.math.BigDecimal = 1.10

scala> val y = new BigDecimal(new JBigD(JBigI.valueOf(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 (by the way RoundingMode enumeration seems to use different names for rounding flags in Scala 2.7 and 2.8):

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
Alexander Azarov