Подтвердить что ты не робот

Scala и Java BigDecimal

Я хочу переключиться с Java на язык сценариев для модулей на основе Math в моем приложении. Это связано с читабельностью и функциональными ограничениями математической 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, чтобы максимизировать читаемость (он использует десятичные числа по умолчанию)?

4b9b3361

Ответ 1

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

Ответ 2

Измените код Scala так:

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

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

Ответ 3

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

В соответствии с ответом Иоакима, пишу val x = BigDecimal(1.1)

эквивалентно написанию

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

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

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

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

Ответ 4

Вы можете хранить значения как 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

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

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