scala中的BigDecimal

时间:2013-10-30 18:35:38

标签: java scala math

我偶然发现了scala中的内容(也许仅适用于我)。总之,如果我们有一个BigDecimal(假设val a = BigDecimal(someValue)其中someValue是十进制字符串)操作的结果

N * a / N == a

并不总是会产生true。我想它与BigDecimals上的任何操作有关。我知道在scala中创建的BigDecimals默认MathContext设置为DECIMAL128HALF_EVEN舍入,精度等于34)。我在小数点后发现了这种行为,点数

后超过30位

我的问题是我得到这样的结果的原因。我可以以某种方式控制它们吗?

示例

-0.007633587786259541984732824427480916

1 个答案:

答案 0 :(得分:4)

正如之前的评论已经指出的那样,无法用无理数来避免这种情况。这是因为无法使用标准数字类型(如果有的话)表示无理数。由于我没有无理数的例子(即使PI限于固定的数字位数,因此可以表示为2个整数的商,使其合理),我将使用重复小数来说明问题。我将N*a/N更改为a/N*N,因为它更好地证明了整数的问题,但它们是等价的:

a = BigDecimal(1)
N = BigDecimal(3)
a/N = 0.333...
a/N*N = 0.999...

正如您在上面的示例中所看到的,您可以使用尽可能多的小数位和任何舍入模式,但结果永远不会等于1.(尽管可以使用不同的舍入模式获得1操作,即BigDecimal(3, roundHalfEven) * (BigDecimal(1, roundUp) / 3)

控制数字比较可以做的一件事是在执行算术运算时使用更高的精度,并在比较时舍入到所需的(更低)精度:

val HighPrecision = new java.math.MathContext(36, java.math.RoundingMode.HALF_EVEN);
val TargetPrecision = java.math.MathContext.DECIMAL128;

val a = BigDecimal(1, HighPrecision)
val N = BigDecimal(3, HighPrecision)
(a/N*N).round(TargetPrecision) == a.round(TargetPrecision)

在上面的示例中,最后一个表达式的计算结果为true

<强>更新

要回答你的评论,虽然BigDecimal是任意精度,但它仍然受到精度的限制。它可以是34或者它可以是1000000(如果你有足够的内存)。 BigDecimal不知道1 / 30.33<repeating>。如果你考虑分裂是如何工作的,那么BigDecimal无法确定地知道它重复而不进行无限小数位的除法。但由于精度为2表示它可以在2位小数后停止分割,因此只知道1 / 30.33