在 groovy/grails 中对 BigDecimal 使用比较运算符安全吗?

is it safe to use comparison operators on BigDecimal in groovy/grails?

比较两个 BigDecimals 的 Java 方法是使用 compareTo() 方法,并检查结果是 -1、0 还是 1。

BigDecimal a = new BigDecimal("1.23")
BigDecimal b = new BigDecimal("3.45")
if (a.compareTo(b) > 0)) { }

我看到有些人在 grails 中使用这种格式:

 if (a > b) {  }

这个工作正常吗? IE。它会得到正确的小数点,还是会转换为浮点数或类似值并进行比较?

使用“==”与使用 equals() 相比如何?

这样的结果是什么:

BigDecimal a = new BigDecimal("1.00")
BigDecimal b = new BigDecimal("1")
assert (a==b)

这似乎有效,但我们已经根深蒂固 Java 不要做这种事情。

+=怎么样?例如

a+=b?

这会和

一样吗
a = a.add(b)

这种东西哪里找的?我有两本 groovy 书,不幸的是,它们都没有提到 BigDecimal 比较或算术,只有 conversion/declaration.

Groovy 允许运算符重载。当一个类型实现某些方法时,您可以在该类型上使用相应的运算符。

对于+实现的方法是plus,不是add。

对于大于或小于比较,Groovy 在对象上查找 compareTo 方法,对于 == 它查找名为 equals 的方法。 (如果你想比较引用,就像在 Java 中使用 ==,你必须使用 is。)

下面是 table 常用数学运算符以及用于重载它们的方法:

Operator     Method
a + b        a.plus(b)
a - b        a.minus(b)
a * b        a.multiply(b)
a / b        a.divide(b)
a++ or ++a   a.next()
a-- or --a   a.previous()
a << b       a.leftShift(b)

你可以看到 BigDecimal 重载了其中一些方法(你得到了加、减、乘和除的运算符重载,但不是 next、previous 或 leftShift):

groovy:000> BigDecimal.methods*.name
===> [equals, hashCode, toString, intValue, longValue, floatValue, doubleValue,
byteValue, shortValue, add, add, subtract, subtract, multiply, multiply, divide,
 divide, divide, divide, divide, divide, remainder, remainder, divideAndRemainde
r, divideAndRemainder, divideToIntegralValue, divideToIntegralValue, abs, abs, m
ax, min, negate, negate, plus, plus, byteValueExact, shortValueExact, intValueEx
act, longValueExact, toBigIntegerExact, toBigInteger, compareTo, precision, scal
e, signum, ulp, unscaledValue, pow, pow, movePointLeft, movePointRight, scaleByP
owerOfTen, setScale, setScale, setScale, stripTrailingZeros, toEngineeringString
, toPlainString, round, compareTo, getClass, notify, notifyAll, wait, wait, wait
, valueOf, valueOf, valueOf]

根据您的对象实现 equals 和 compareTo 的方式,您可以得到 ==><>=<=

因此运算符导致调用已在 BigDecimal 中声明或由 groovy 添加到 BigDecimal 的方法。为了能够在基元上使用运算符,它绝对不会对像 float 这样的基元类型进行任何类型的转换。

table 取自 this developerworks article by Andrew Glover and Scott Davis,其中包含更多详细信息和示例代码。

Groovy 在管理数字和无限精确方面做得非常出色。您应该知道的第一件事是,任何带点的数字在默认情况下都是 BigDecimal——无限精度的原因。这是一个确切含义的示例。考虑这个片段:

    System.out.println(2.0 - 1.1);
    System.out.println(new BigDecimal(2.0).subtract(new BigDecimal(1.1)));
    System.out.println(new BigDecimal("2.0").subtract(new BigDecimal("1.1")));

    // the above will give these:
    0.8999999999999999
    0.899999999999999911182158029987476766109466552734375
    0.9

这显示了我们在 Java 中获得体面的东西所必须经历的耐力。在 Groovy 中,您只需执行以下操作:

println 2 - 1.1​

得到你的0.9!在 Groovy web console 上试试这个。在这里,第二个操作数是一个 BigDecimal,所以整个计算都在 BigDecimal 中,精度是 Groovy 在这种情况下会努力完成干净。

但是怎么办?这是因为几乎 Groovy 中的每个运算符都映射到引擎盖下对象的方法调用,因此 a + ba.plus(b ),而 a==b 转换为 a.compareTo(b)。因此,假设您的假设是安全的,这就是 Groovy 做事的方式:少写,表达,Groovy 会为您完成工作。您可以在 Groovy-lang documentation page 中了解所有这些内容,并提供全面的示例。

简短的回答是 ,在 Groovy 中使用 == 进行 BigDecimal 比较是安全的。

来自 Groovy 文档 Behaviour of == 部分:

In Java == means equality of primitive types or identity for objects. In Groovy == translates to a.compareTo(b)==0, if they are Comparable, and a.equals(b) otherwise. To check for identity, there is is. E.g. a.is(b).