我偶然发现了scala中的内容(也许仅适用于我)。总之,如果我们有一个BigDecimal(假设val a = BigDecimal(someValue)
其中someValue
是十进制字符串)操作的结果
N * a / N == a
并不总是会产生true
。我想它与BigDecimals上的任何操作有关。我知道在scala中创建的BigDecimals默认MathContext设置为DECIMAL128
(HALF_EVEN
舍入,精度等于34)。我在小数点后发现了这种行为,点数
我的问题是我得到这样的结果的原因。我可以以某种方式控制它们吗?
示例
-0.007633587786259541984732824427480916
答案 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 / 3
是0.33<repeating>
。如果你考虑分裂是如何工作的,那么BigDecimal无法确定地知道它重复而不进行无限小数位的除法。但由于精度为2表示它可以在2位小数后停止分割,因此只知道1 / 3
为0.33
。