BigDecimal.divide(...) - 具有非终止扩展的商的适当比例

时间:2011-12-19 12:33:12

标签: java floating-point bigdecimal

我使用BigDecimal进行浮点数学运算。如果您将5除以4.2,您将获得例外(因为结果具有无法终止的扩展,无法用BigDecimal表示),即

BigDecimal five = new BigDecimal("5");
BigDecimal fourPointTwo = new BigDecimal("4.2");
five.divide(fourPointTwo) // ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

我准备在这种情况下失去一些精确度,因此我将使用divide(...)方法,该方法允许提供结果的比例:

five.divide(fourPointTwo, 2, RoundingMode.HALF_UP); //Fine, but obviously not 100% accurate

我应该向这个方法传递什么比例,以便结果与使用两个doubles执行计算一样准确?

2 个答案:

答案 0 :(得分:2)

来自javadocs of the BigDecimal class

  

如果为零或正数,则比例是小数点右侧的位数。如果是负数,则将数字的未缩放值乘以10来表示比例的否定。因此,BigDecimal表示的数字的值是(unscaledValue×10 -scale )。

double的精度根据值的数量级而变化。根据{{​​3}},它使用52位来存储无符号尾数,因此任何可以用52位表示的整数都可以。这大约是18位小数。

此外,double使用11位来存储指数。所以,像4小数精度的东西会做。这样,可以表示任何高达52位的整数乘以2的正或负幂,最多10位(一位是expoent的符号)。除此之外,你开始失去精确度。

double的额外位存储符号。

这样,比例18 + 4 = 22将至少与double一样精确。

答案 1 :(得分:1)

您的问题称为“舍入错误”或“舍入错误”。例如:

您有两个号码ab。你知道每个都有一定的精度(即你有信心的位数),这意味着每隔一个数字都是“随机”的噪音。

想象一下b的精度为两位数。 (b*100)-int(b*100)的结果将是随机的,因为该操作会删除所有“正确”数字。

这些错误根据数学运算传播。一些例子:

  • 添加数字时会添加错误边距。如果ab的精度为2,则添加它们可能会将分数的第二个数字变为垃圾:0.003 + 0.008 = 0.011

  • 乘法可以快速增加误差,指数函数可以更快地增加误差。

  • 除法减少误差范围(0.003 / 3 = 0.001)

因此,如果您需要正确答案,则必须按照上述规则计算代码中所有操作的误差范围。 链接任何人?

当然,这通常不是一种选择。因此,您需要考虑可以忍受的错误量。例如,如果您对财务数据进行数学计算,精度为10或20通常就足够了,因为在错误成为值的重要部分之前,您有足够的位来“浪费”几个数学运算。

示例:您从10.500 000 0003.100 000 000开始。如果你将两者分开,你会得到3.387 096 774。从那里开始,你只需要3.87 - 其余的是备用精度,你可以在进一步的操作中使用它,直到你将最后一个结果四舍五入并将其保存回数据库。