Scala and Java BigDecimal

为君一笑 提交于 2019-12-05 08:54:45

问题


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


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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



回答5:


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>



回答6:


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 Ms -- 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


来源:https://stackoverflow.com/questions/2697638/scala-and-java-bigdecimal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!