Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala and Java BigDecimal

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

like image 637
geejay Avatar asked Apr 23 '10 09:04

geejay


People also ask

What is Scala BigDecimal?

BigDecimal represents decimal floating-point numbers of arbitrary precision. By default, the precision approximately matches that of IEEE 128-bit floating point numbers (34 decimal digits, HALF_EVEN rounding mode).

What is a Java BigDecimal?

A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale.

How does BigDecimal compare to Java?

BigDecimal. compareTo(BigDecimal val) compares the BigDecimal Object with the specified BigDecimal value. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method.

Can you add BigDecimal in Java?

add(BigDecimal val) is used to calculate the Arithmetic sum of two BigDecimals. This method is used to find arithmetic addition of large numbers of range much greater than the range of largest data type double of Java without compromising with the precision of the result.


4 Answers

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.

like image 135
Joachim Sauer Avatar answered Oct 06 '22 12:10

Joachim Sauer


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.

like image 40
Jesper Avatar answered Oct 06 '22 11:10

Jesper


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.

like image 20
Kevin Wright Avatar answered Oct 06 '22 10:10

Kevin Wright


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
like image 43
Alexander Azarov Avatar answered Oct 06 '22 11:10

Alexander Azarov