我使用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
执行计算一样准确?
答案 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)
您的问题称为“舍入错误”或“舍入错误”。例如:
您有两个号码a
和b
。你知道每个都有一定的精度(即你有信心的位数),这意味着每隔一个数字都是“随机”的噪音。
想象一下b
的精度为两位数。 (b*100)-int(b*100)
的结果将是随机的,因为该操作会删除所有“正确”数字。
这些错误根据数学运算传播。一些例子:
添加数字时会添加错误边距。如果a
和b
的精度为2,则添加它们可能会将分数的第二个数字变为垃圾:0.003 + 0.008 = 0.011
乘法可以快速增加误差,指数函数可以更快地增加误差。
除法减少误差范围(0.003 / 3 = 0.001)
因此,如果您需要正确答案,则必须按照上述规则计算代码中所有操作的误差范围。 链接任何人?
当然,这通常不是一种选择。因此,您需要考虑可以忍受的错误量。例如,如果您对财务数据进行数学计算,精度为10或20通常就足够了,因为在错误成为值的重要部分之前,您有足够的位来“浪费”几个数学运算。
示例:您从10.500 000 000
和3.100 000 000
开始。如果你将两者分开,你会得到3.387 096 774
。从那里开始,你只需要3.87
- 其余的是备用精度,你可以在进一步的操作中使用它,直到你将最后一个结果四舍五入并将其保存回数据库。