【问题标题】:Prevent underflow in floating point division in Python在 Python 中防止浮点除法中的下溢
【发布时间】:2018-06-19 04:41:51
【问题描述】:

假设xy 都是非常小的数字,但我知道x / y 的真实值是合理的。

计算x/y 的最佳方法是什么? 特别是,我一直在做np.exp(np.log(x) - np.log(y),但我不确定这是否会有所作为?

【问题讨论】:

  • 你能举一个例子,说明这会失败的两个数字吗? IEEE 浮点通常非常稳健。你是说xy 都太小而不能用 IEEE 浮点数表示?
  • 实际上,我没有具体的失败示例---我只是担心x/y 会失败并且一直在做np.exp(np.log(x) - np.log(y) 但后来我想知道这是否会有所作为一点也不。让我稍微编辑一下这个问题。
  • 我无法想象使用logexp 会比直接除法产生更好的结果。众所周知,我有时缺乏想象力。
  • @MarkRansom 你肯定是对的,但我也不确定......

标签: python floating-point precision underflow


【解决方案1】:

根据Python documentation,Python 使用了它所运行的硬件的浮点特性。在当今最常见的机器上,这是 IEEE-754 算术或类似的算法。该 Python 文档没有明确说明舍入模式,但顺便提到了样本除法的结果是最接近的可表示值,因此 Python 可能使用舍入到最近的关系到偶数模式。 (简称“四舍五入”。如果两个可表示的值在二进制浮点中同样接近,则产生其有效数的低位为零的值。)

在四舍五入模式下的 IEEE-754 算术中,除法的结果是最接近精确数学值的可表示值。既然您说x/y 的数学值是合理的,那么它就在可表示值的正常范围内(不低于它,处于亚正常范围内,精度会受到影响,而不是高于它,结果四舍五入到无穷大)。在正常范围内,基本运算的结果将在格式的正常精度范围内准确。

但是,由于 xy 是“非常小的数字”,我们可能会担心它们是次正规的,并且在执行除法之前它们已经损失了精度。在 IEEE-754 基本 64 位二进制格式中,低于 2-1022(约 2.22507•10-308)的数字是次常的。如果xy 小于这个值,那么它们已经损失了精度,除了偶然之外,没有任何方法可以从中得出正确的商。取对数来计算商将无济于事。

如果您运行的机器碰巧没有使用 IEEE-754,那么直接计算 x/y 仍然可能会产生比 np.exp(np.log(x)-np.log(y)) 更好的结果。前者是在硬件中计算基本功能的单一操作,可能是经过合理设计的。后者是在软件中计算复杂函数的几种操作,使用普通硬件操作难以准确计算。

对浮点运算存在相当多的不安和不信任。缺乏知识似乎导致人们害怕他们。但是这里应该理解的是,基本的浮点运算定义得非常好,并且在正常范围内是准确的。浮点计算的实际问题来自运算序列上舍入误差的累积、复合误差的内在数学以及对结果的不正确预期。这意味着无需担心单个划分的准确性。相反,应该牢记浮点的整体使用。 (如果提供更多上下文,说明为什么这个划分很重要,xy 是如何从先前的数据中产生的,以及总体目标是什么,你的问题可能会得到更好的回答。)

注意

与 IEEE-754 的一个常见偏差是将次正常值刷新为零。如果您有一些不正常的xy,则某些实现可能会在对它们执行操作之前将它们刷新为零。但是,这在 SIMD 代码中比在普通标量编程中更常见。而且,如果它发生了,它会阻止你评估np.log(x)np.log(y),因为在这些值中,次正常值也会被刷新为零。所以我们可以排除这种可能性。

【讨论】:

  • 你确定取对数没有帮助吗?虽然 x 和 y 是非正规的,但它们的对数不是,因此尽管计算更复杂,但使用它们可能会得到更好的结果。
  • @RudyVelthuis:它有什么帮助?假设遵循 IEEE 754 语义,直接除法的结果已经是最好的(最接近精确商的可表示数字)。
  • 您为什么认为对数会产生更好的结果?在 IEEE-754 中,x 和 y 相除的结果是最接近数学精确值的可表示值。 不存在更接近的可表示值。因此不可能以浮点格式返回更好的结果。
  • @MarkDickinson:我假设 x 和 y 值是非正规的,因此非常不精确。但是,如果它们是使用对数计算的,则对数不是非正规的。减法应该仍然具有良好的精度,并且求幂应该仍然比简单的 denormals 的除法更好。
  • @RudyVelthuis:如果 x 是 pow(10, -300) 的结果,y 是 pow(10, -308) 的结果,则 x 除以 y 将产生可表示的结果,即最接近 x/y 的精确数学结果。 不存在更好的结果。如果您的抱怨是浮点x/y 会出现一些与pow(10, 8) 不同的错误,那是因为当您分开他们。如果 x 和 y 中已经存在错误,则取对数、减法和求幂不会消除这些错误
【解决方案2】:

除法与其他 IEEE-754 指定的运算一样,以无限精度计算,然后(使用普通舍入规则)舍入到最接近的可表示浮点数。计算 x/y 的结果几乎肯定会比计算 np.exp(np.log(x) - np.log(y) 的结果准确得多(并且保证不会低于准确)。

【讨论】:

  • Python 使用底层硬件浮点。在大多数常见机器上它可能是 IEEE-754,但不能保证。
  • 我对“无限精度”有点问题。精度通常高于两倍,但不是“无限”。
  • @RudyVelthuis 不,它是无限。显然,当您计算像1/3 这样的东西时,实际上不可能在四舍五入之前有一个无限的10101010101... 序列,但处理器必须找到一种方法来计算最终值,就好像它确实做到了一样。所有错误都必须来自四舍五入,而不是来自计算。 FWIW,“无限精度”的措辞直接来自 IEEE-754 标准。
  • 处理器简单地通过计算一些额外的精度位(例如 64 位有效位而不是 53 位)来完成此操作,然后将这些四舍五入到所需的精度。我不知道 IEEE-745 是否这样描述它,但现实是这根本不会发生。
  • @RudyVelthuis:IEEE-754 需要一个实现来计算结果好像精确的数学结果是以无限精度计算的,然后四舍五入到最接近的可表示值。为了在硬件中实现这一点,设计人员计算他们需要使用多少位数和数据才能获得所需的结果。如果他们使用固定数量的扩展精度,则他们已经获得证明,证明他们使用的精度足以获得所需的结果。
猜你喜欢
  • 1970-01-01
  • 2014-06-28
  • 2023-03-21
  • 1970-01-01
  • 2012-08-25
  • 2015-09-27
  • 2023-03-16
  • 1970-01-01
  • 2021-11-06
相关资源
最近更新 更多