Scala和Java BigDecimal

Scala和Java BigDecimal,java,math,scala,groovy,bigdecimal,Java,Math,Scala,Groovy,Bigdecimal,我想为我的应用程序中基于数学的模块从Java切换到脚本语言。这是由于MathyJava的可读性和功能限制 例如,在Java中,我有: BigDecimal x = new BigDecimal("1.1"); BigDecimal y = new BigDecimal("1.1"); BigDecimal z = x.multiply(y.exp(new BigDecimal("2")); 如您所见,如果没有BigDecimal运算符重载,简单的公式很快就会变得复杂 对于双打,这看起来不错,但

我想为我的应用程序中基于数学的模块从Java切换到脚本语言。这是由于MathyJava的可读性和功能限制

例如,在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)
使用
double
值初始化
BigDecimal
,因此它并不完全等于1.1。在Java中,您必须使用新的BigDecimal(“1.1”)。也许这对Scala也有帮助。

将Scala代码更改为:

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

在这方面,Scala与Java完全相同

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>
根据约阿希姆的回答,写
valx=BigDecimal(1.1)

相当于写作

val d : Double = 1.1
val x = BigDecimal(d)
当然,问题是Double
d
已经存在舍入错误,因此您正在使用错误数据初始化x

使用接受字符串的构造函数,一切都会好起来


根据您的示例,您最好使用
val
s而不是
var
s,并且您也可以在Scala中安全地去掉分号。

您可以在内部将值存储为整数/字符串(无精度),并使用
scale
(这是Scala REPL的一个抄本):

或者,如果要分析字符串,可以控制舍入:

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

我知道这个问题已经很老了,而且已经得到了回答,但是如果你对不同的语言持开放态度(就像OP所说的那样),另一个选择是使用Clojure。依我看,Clojure有一些最简单的
BigDecimal
math语法(注意后面的
M
s——表示
BigDecimal
):

我喜欢Clojure for math,因为它在许多情况下默认为精确,例如使用
比率

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

你真的确定使用BigDecimal是正确的吗?你在做什么样的计算?我无法验证Scala中BigDecimal的问题。至少在2.8.1var中,它可以正常工作x:BigDecimal=11.0应该是精确的,x=x/10.0应该是满意的。这实际上并没有回答这个问题,它只是演示了一种不同的方法来使用字符串构造函数,正如在不同的答案中已经介绍过的那样!伙计,真难看。我想如果您关心十进制精度,那么使用Groovy是迄今为止最漂亮的选择。它对BigDecimal的支持还不完全完善,但比任何其他语言都要好得多。@mcv发生的事情是,在
BigDecimal(1.1)
中,表达式1.1被解析为双精度,甚至在
BigDecimal
获得其值之前。因此,您需要
BigDecimal(“1.1”)
,以便
BigDecimal
能够负责决定“1.1”的含义。这是一个普遍的问题(在任何语言的BigDecimal或等效语言中),AFAICT只能用不自动将数字文本读取为浮点值的语言来解决(我一直认为这会避免很多混淆),但我不知道有哪种语言是这样做的。@shrevatsar这正是Groovy所做的。它具有本机BigDecimal支持,并立即将1.1解释为BigDecimal。这可能会让习惯本机浮动的人感到困惑,但在业务逻辑中,这通常是您真正想要的。我无法验证Scala中BigDecimal的问题。它按照2.8.1中的预期工作,但不是等效的。在第二种情况下,您使用一个新变量
d
。为什么要使用
val x=new BigDecimal(new JBigD(JBigI.valueOf(110),Scale))
而不是
val x=BigDecimal(110,Scale)
?谢谢您的指点!我记不起Scala2.7.x中是否提供了这种方法,但在Scala2.8中肯定是这样。我会修正答案。肯定比现在的Scala版本要好——如果你喜欢波兰语符号。@ziggystar是的,你必须愿意接受前缀/波兰语符号,这对许多人来说可能是一个很大的障碍:)我只是想指出,因为Clojure具有很强的可塑性,它可以在宏中使用中缀符号。显示了几年前白炽灯库中的一个示例。这里发生了什么?为什么
new
既不需要也不被接受?请注意
BigDecimal(“1.0”)
without
new
BigDecimal.apply(“1.0”)
-它调用
对象BigDecimal
apply
方法,该方法是
类BigDecimal
的伴生对象。
apply
方法是工厂方法。它不适用于
new
(至少在Scala 2.10.0中不适用),因为显然
BigDecimal
没有接受
字符串的公共构造函数。这可能是Scala版本的
BigDecimal
中的一个bug。请注意,如果您使用
newjava.math.BigDecimal(“1.0”)
,它就会工作。这是一个不同的类。我在哪里可以了解无构造函数版本的工作原理?请参阅object的API文档。其中
BigDecimal(…)
的表示法与
BigDecimal.apply(…)
的意思相同,这是Scala编译器的神奇之处。
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
user=> (/ 60 14)
30/7
user=> (type (/ 60 14))
clojure.lang.Ratio