【发布时间】:2018-11-12 16:29:34
【问题描述】:
我时不时地看到一些舍入错误,这些错误是由以下两个示例所示的某些值的取整引起的。
// floor(number, precision)
double balance = floor(0.7/0.1, 3) // = 6.999
double balance = floor(0.7*0.1, 3) // = 0.069
问题当然是0.7/0.1 和0.7*0.1 由于表示错误而不完全是应该的数字[检查下面的注释]。
一种解决方案可能是添加一个 epsilon,以便在应用底限之前减轻任何表示错误。
double balance = floor(0.7/0.1 + 1e-10, 3) // = 7.0
double balance = floor(0.7*0.1 + 1e-10, 3) // = 0.07
我应该使用什么 epsilon 才能保证在所有情况下都能正常工作?我觉得这个解决方案相当老套,除非我有一个很好的策略来选择正确的 epsilon,这可能取决于我正在处理的数字。
例如,如果有一种方法可以估计错误(如representation - number)或至少是它的符号(无论representation > number 与否),这将有助于确定哪个方向我应该在应用 floor 之前更正结果。
您能想到的任何其他解决方法都非常受欢迎。
注意:我知道真正的问题是我使用的是双打并且它有表示错误。请不要说我应该将余额长期存储((long) Math.floor(3931809L/0.080241D) 同样不稳定)。我也尝试使用 BigDecimal,但性能下降了很多(它是一个实时应用程序)。另外,请注意,我不太关心随着时间的推移传播小错误,我做了很多类似上面的计算,但我每次都从一个新的余额数开始(在返回和重新开始之前我可能会做 3 次这些操作)。
编辑:为了清楚起见,这是我做的唯一操作,我在同一天平上重复了 3 次。例如,我以美元结余,然后将其转换为卢布,然后转换为日元,然后转换为欧元,然后我返回余额并从头开始(使用新的余额编号,即除了 on 之外没有传播舍入误差这 3 个操作)。除了它们是正数(即在 [0, +inf) 范围内)并且精度始终低于 8(8 个十进制数字,即 0.00000001 是我必须处理的最小余额)之外,这些值不受限制.
【问题讨论】:
-
如果您感兴趣的只是将值转换回整数,那么使用Math.round 有什么问题?它会将高于 0.5 的所有内容四舍五入,并将低于 0.5 的所有内容舍入,这为小错误提供了足够的余地。
-
@SergGr 我不认为他只是在转换为整数。
-
@LAD,它可能不仅是关于转换为整数,而且现在每个示例都显示转换为
long,然后除以1000.0,我认为这是一个常量表示所需精度的上下文。 -
@SergGr 嗯,是的,如果变量被初始化为双精度值,这不太有意义。
-
@SergGr 我没有将值转换回整数,您为什么会得出这样的结论? “(long)”只影响分子,然后我有一个“/ 1000.0”,它将它转换回双精度(即强制转换仅用于截断数字)。这是一种简化,实际上我传递了一个精度,该精度被转换为在地板上使用的乘数 (10^p),但这无关紧要。我希望不是你否决了这个问题。干杯:)
标签: java math double currency epsilon