Scala和Java BigDecimal
我想为我的应用程序中基于数学的模块从Java切换到脚本语言。这是由于MathyJava的可读性和功能限制 例如,在Java中,我有: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运算符重载,简单的公式很快就会变得复杂 对于双打,这看起来不错,但
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)
当然,问题是Doubled
已经存在舍入错误,因此您正在使用错误数据初始化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”)
withoutnew
是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