Scala и Java BigDecimal - PullRequest
       16

Scala и Java BigDecimal

28 голосов
/ 23 апреля 2010

Я хочу переключиться с Java на язык сценариев для модулей на основе математики в моем приложении.Это связано с удобочитаемостью и функциональными ограничениями математической Java.

Например, в Java у меня есть это:

BigDecimal x = new BigDecimal("1.1");
BigDecimal y = new BigDecimal("1.1");
BigDecimal z = x.multiply(y.exp(new BigDecimal("2"));

Как видите, без перегрузки оператора BigDecimal, простые формулывсе усложняется очень быстро.

С парой это выглядит хорошо, но мне нужна точность.

Я надеялся, что в Scala я смогу сделать это:

var x = 1.1;
var y = 0.1;
print(x + y);

по умолчанию я получаю десятичное поведение, увы Scala не использует десятичные вычисления по умолчанию.

Затем я делаю это в Scala:

var x = BigDecimal(1.1);
var y = BigDecimal(0.1);
println(x + y);

И я все еще получаю неточный результат.

Есть ли что-то, что я не правильно делаю в Scala?

Может быть, мне следует использовать Groovy для максимальной читабельности (по умолчанию используются десятичные дроби)?

Ответы [ 6 ]

38 голосов
/ 23 апреля 2010

Я не знаю Scala, но в Java new BigDecimal(1.1) инициализирует BigDecimal значением double и, таким образом, оно не точно , равное 1.1.В Java вы должны использовать new BigDecimal("1.1") вместо этого.Может быть, это поможет и в Scala.

31 голосов
/ 23 апреля 2010

Измените свой код Scala на это:

var x = BigDecimal("1.1");   // note the double quotes
var y = BigDecimal("0.1");
println(x + y);

и будет работать так же, как в Java.

13 голосов
/ 23 апреля 2010

Scala в этом отношении определенно совпадает с Java.

Согласно ответу Иоахима, запись val x = BigDecimal(1.1)

эквивалентна записи

val d : Double = 1.1
val x = BigDecimal(d)

проблема, конечно, в том, что Double d ALREADY имеет ошибку округления, поэтому вы инициализируете x с неверными данными.

Используйте вместо этого конструктор, который принимает строку, и все будет хорошо.

Учитывая ваш пример, вам также было бы лучше использовать val s вместо var s, и вы также можете безопасно оставить точки с запятой в Scala.

6 голосов
/ 23 апреля 2010

Вы можете хранить значения как Integer / String (без точности) внутри и использовать scale (это стенограмма из 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)

Или, если вы хотите разобрать строку, вы можете контролировать округление:

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 голосов
/ 23 апреля 2010
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>
2 голосов
/ 06 июня 2011

Я знаю, что этот вопрос старый и на него дан ответ, но другой вариант, если вы открыты для разных языков (как казалось бы OP), - это использовать Clojure.У Clojure, IMO, есть один из самых простых синтаксисов для BigDecimal математики (обратите внимание на завершающий M s - который указывает 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

Мне нравится Clojure для математики, поскольку по умолчаниюточность во многих случаях, например, использование Ratio:

user=> (/ 60 14)
30/7
user=> (type (/ 60 14))
clojure.lang.Ratio
...