首页 文章

scala中的BigDecimal

提问于
浏览
3

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

N * a / N == a

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

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

example

-0.007633587786259541984732824427480916

1 回答

  • 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 .

    UPDATE

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

相关问题