【问题标题】:Java Estimating Double's Representation ErrorJava 估计 Double 的表示错误
【发布时间】: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.10.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


【解决方案1】:

我应该使用什么 epsilon 才能保证在所有情况下都能正常工作?

没有保证在所有情况下都能正常工作的 epsilon1。期间。

如果您分析(数学上2)您的应用程序正在执行的计算,那么可能找出适合您的 epsilon 值。

但请注意,在多步计算中反复“四舍五入”错误是有危险的。你最终可能会得到错误的答案。这就是数学所说的。

最后,问问自己这个问题:如果只进行基于 epsilon 的调整是合法/安全的,为什么(50 年后)典型的手持计算器仍然坚持1.0 / 3 * 30.9999999999....


1 - 让我们清楚。您还没有尝试指定您的“案例”是什么。所以我假设你的意思是所有可能的计算。

2 - 由于 Real 数和相应的浮点二进制表示(例如“double”)之间的 epsilon 取决于数的二进制大小,因此分析变得复杂.

【讨论】:

  • 我知道所有可能的双精度计算都没有灵丹妙药 epsilon,但我的问题比一般情况要具体得多。在这种情况下(即,给定舍入函数floorbalancerateprecision),可能有一种方法可以找到在所有情况下都可以正常工作的 epsilon。我只需要在同一天平上执行几次此操作,我认为floor 引入的误差远大于使用双精度数引入的任何舍入误差。
  • 您的问题可能更具体,但您没有告诉我们它是什么。除非您能清楚/准确/准确地告诉我们它是什么,否则我们无法进行分析以确定 1)您的方法是否有效,以及 2)epsilon 应该是什么。
  • 在什么方面不具体?让我知道我可以添加哪些信息,我会编辑问题。
  • "我只需要在同一个天平上做几次这个操作,我认为地板引入的误差远大于使用双精度数引入的任何舍入误差。 " - 不够详细。我们需要精确的操作顺序(包括“几次”的含义)和所涉及的数字范围。这是我们在这里讨论的正确数学,而不是一些 “我认为错误......” 东西。“我认为” 基本上是猜测,
  • 我在原始问题中有该信息。操作始终是balance = floor(balance * rate, precision),我应用它的次数通常是 3(也可能是 1 或 2,但不会更多)。换句话说,假设您有美元余额,然后将其转换为卢布(一次操作),然后您以卢布获得该余额并将其转换为日元(第二次操作),然后您以日元获得该余额并且您将其转换为欧元(第三次操作)。对于某些货币,我需要很高的精度,我使用的是 8 位十进制数字。所有其他值都不受限制(除了它们是正数)。
【解决方案2】:

二进制浮点数(双精度)具有 53 位精度或大约 15.95 个十进制数字。

令 r 为 Real,d 为最接近 r 的double

epsilon(r) = |r - d|在 0 到 r * 2floor(log2(r)) -53

的范围内

并且,如果您必须处理 0 到 N 范围内的数字,则该范围内的最大 epsilon 值将约为:

N * 2楼层(log2(N)) - 53

执行计算时,您需要估计计算中所有步骤的累积误差。加法、乘法和除法相对“安全”。例如乘法:

令 r1 = d1 + e1 且 r2 = d2 + e2

r1 * r2 = (d1 + e1) * (d 2 + e2) = d1 * d2 + d2 * e1 + d1 * e2 + e1 * e2

除非 epsilon 值已经很大,否则 e1 * e2 项相对于其他项消失,并且 epsilon 将 ≤ 2 * max( |d1|, |d2|) * max(|e1|, |e2| )。

(我想。自从我上次愤怒地做这件事以来已经很久很久了。)

但是,减法具有令人讨厌的特性;参见 Goldberg 论文的定理 9!

你使用的floor函数也有点棘手。

令 r = d + e

epsilon(floor(r)) = |floor(r, 3) - floor(d, 3)|

≤max(|ceiling(e, 3)|, 10-3)

【讨论】:

    猜你喜欢
    • 2011-10-19
    • 2016-12-20
    • 1970-01-01
    • 2018-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-25
    • 1970-01-01
    相关资源
    最近更新 更多