Ответ 1
Я не знаю Scala, но в Java new BigDecimal(1.1)
инициализирует BigDecimal
значением double
и, следовательно, он не точно равен 1.1. В Java вы должны использовать new BigDecimal("1.1")
. Возможно, это поможет и в Scala.
Я хочу переключиться с 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, чтобы максимизировать читаемость (он использует десятичные числа по умолчанию)?
Я не знаю Scala, но в Java new BigDecimal(1.1)
инициализирует BigDecimal
значением double
и, следовательно, он не точно равен 1.1. В Java вы должны использовать new BigDecimal("1.1")
. Возможно, это поможет и в Scala.
Измените код Scala так:
var x = BigDecimal("1.1"); // note the double quotes
var y = BigDecimal("0.1");
println(x + y);
и он будет работать так же, как в Java.
Scala в этом отношении определенно совпадает с Java.
В соответствии с ответом Иоакима, пишу val x = BigDecimal(1.1)
эквивалентно написанию
val d : Double = 1.1
val x = BigDecimal(d)
Проблема, конечно, в том, что Double d
ALREADY имеет ошибку округления, поэтому вы инициализируете x с плохими данными.
Используйте конструктор, который принимает строку вместо этого, и все будет нормально.
В соответствии с вашим примером вам также лучше использовать val
вместо var
s, и вы можете спокойно оставить точки с запятой в Scala.
Вы можете хранить значения как 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
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>
Я знаю, что этот вопрос старый и ответил, но другой вариант, если вы открыты к разным языкам (как казалось бы 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