web-dev-qa-db-ja.com

ScalaおよびJava BigDecimal

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を使用する必要があるかもしれません(デフォルトでは小数を使用します)?

28
geejay

Scalaはわかりませんが、Java new BigDecimal(1.1)ではBigDecimaldouble値で初期化するため、-ではありません。 正確に 1.1に等しい。Javaでは、代わりにnew BigDecimal("1.1")を使用する必要があります。多分それはScala as上手。

38
Joachim Sauer

Scalaコードを次のように変更します:

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

javaの場合と同じように機能します。

31
Jesper

Scalaは、この点でJava)と間違いなく同じです。

Joachimの答えによると、val x = BigDecimal(1.1)と書く

書くことと同等です

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

もちろん、問題は、Double dに丸め誤差があるため、xを不正なデータで初期化していることです。

代わりに文字列を受け入れるコンストラクターを使用してください。すべて問題ありません。

あなたの例を考えると、valsの代わりにvarsを使用したほうがよいでしょう。また、Scalaでもセミコロンを安全にオフのままにしておくことができます。

13
Kevin Wright

値を整数/文字列(精度なし)として内部的に格納し、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
6
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>
5
Eastsun

私はこの質問が古くて答えられていることを知っていますが、(OPがそうであるように)異なる言語を受け入れる場合、別のオプションはClojureを使用することです。 Clojureには、IMO、BigDecimal mathの最も単純な構文がいくつかあります(末尾のMs-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
2
overthink