数值编程语言是否区分“最大有限数”和“无穷大”?

时间:2015-04-25 00:11:42

标签: python matlab numpy integer-overflow

问题动机:

在我所知道的标准数字语言中(例如Matlab,Python numpy等),例如,如果你取一个适度大数的指数,那么输出就是数值溢出的结果。如果将此值乘以0,则得到NaN。另外,这些步骤足够合理,但它们揭示了数学实现中的逻辑错误。由溢出产生的第一个数字已知是有限的,我们显然希望乘以0的结果,这个大的有限数为0.

显式:

>>> import numpy as np
>>> np.exp(709)*0
0.0
>>> np.exp(710)*0
nan

我想我们可以在这里介绍一个具有以下特性的“最大有限值”(LFV)概念:

  • LFV将是数字溢出的默认值,否则将是 圆形到无穷大
  • LFV<无限
  • 任何明确的数字< LFV(例如,如果LEV代表“最大显式值”,则LEV
  • (MATLAB详细信息:realmax< LFV)
  • LFV * 0 = 0

另一方面,不应简单地以LFV描述的方式重新定义无穷大。对于0 * infinity = 0没有意义......适当地,当前的无穷大标准实现在此设置中产生NaN。此外,有时需要将数字初始化为无穷大,并且您需要任何数值运算的结果,即使产生LFV的结果严格小于初始化值(这对于某些逻辑语句也很方便)。我确定存在其他需要适当无穷大的情况 - 我的观点是,无限应该简单地重新定义为具有上面的一些LFV属性。

问题:

我想知道是否有任何语言使用这样的方案,如果这样的方案有任何问题。在正确的数学中不会出现这个问题,因为对数字的大小没有这些数值限制,但我认为在编程语言中实现一致的数学时这是一个真正的问题。基本上,通过LFV,我想我想要最大显式值和无穷大LFV =(LEV,无穷大)之间的开放区间的简写,但也许这种直觉是错误的。

更新:在评论中,人们似乎反对我提出的问题的实用性。我的问题不是因为发生了许多相关问题,而是因为在许多不同的环境中经常出现同样的问题。通过与进行数据分析的人交谈,这在训练/拟合模型时经常足以导致运行时错误。问题基本上是为什么这不是由数字语言处理的。从评论中,我基本上认为编写语言的人看不到以这种方式处理事物的效用。在我看来,当某些特定问题经常发生在使用某种语言的人身上时,以原则方式处理这些异常可能是有意义的,因此每个用户都不必这样做。

1 个答案:

答案 0 :(得分:7)

所以...我好奇并且挖了一点。

正如我在评论中已经提到的那样,"最大的有限值"如果考虑exception status flags,IEEE 754中存在类型。设置了溢出标志的无穷大值对应于您建议的LFV,区别在于该标志仅可在操作后读出,而不是作为值本身的一部分存储。这意味着您必须手动检查标志并在发生溢出时采取行动,而不是仅仅内置LFV * 0 = 0。

关于异常处理及其在编程语言中的支持,有一个非常有趣的paper。引用:

  

设置标志并返回无限或静音NaN的IEEE 754模型假设用户经常(或至少适当地)测试状态。诊断原始问题需要用户检查所有结果是否有异常值反过来假设它们通过所有操作渗透,从而可以标记错误的数据。鉴于这些假设,一切都应该有效,但不幸的是它们不太现实。

本文还哀叹对浮点异常处理的支持不足,特别是在C99和Java中(我确定大多数其他语言都不是更好)。鉴于此,尽管如此,没有主要的努力来解决这个问题或创建一个更好的标准,对我来说似乎表明IEEE 754及其支持在某种意义上是足够好的" (稍后会详细介绍)。

让我给出一个解决你的示例问题的方法来演示一些东西。我使用numpy的seterr使其在溢出时引发异常:

import numpy as np

def exp_then_mult_naive(a, b):
    err = np.seterr(all='ignore')
    x = np.exp(a) * b
    np.seterr(**err)
    return x

def exp_then_mult_check_zero(a, b):
    err = np.seterr(all='ignore', over='raise')
    try:
        x = np.exp(a)
        return x * b
    except FloatingPointError:
        if b == 0:
            return 0
        else:
            return exp_then_mult_naive(a, b)
    finally:
        np.seterr(**err)

def exp_then_mult_scaling(a, b):
    err = np.seterr(all='ignore', over='raise')
    e = np.exp(1)
    while abs(b) < 1:
        try:
            x = np.exp(a) * b
            break
        except FloatingPointError:
            a -= 1
            b *= e
    else:
        x = exp_then_mult_naive(a, b)
    np.seterr(**err)
    return x

large = np.float_(710)
tiny = np.float_(0.01)
zero = np.float_(0.0)

print('naive: e**710 * 0 = {}'.format(exp_then_mult_naive(large, zero)))
print('check zero: e**710 * 0 = {}'
    .format(exp_then_mult_check_zero(large, zero)))
print('check zero: e**710 * 0.01 = {}'
    .format(exp_then_mult_check_zero(large, tiny)))
print('scaling: e**710 * 0.01 = {}'.format(exp_then_mult_scaling(large, tiny)))

# output:
# naive: e**710 * 0 = nan
# check zero: e**710 * 0 = 0
# check zero: e**710 * 0.01 = inf
# scaling: e**710 * 0.01 = 2.233994766161711e+306
  • exp_then_mult_naive执行您所执行的操作:将溢出的表达式乘以0并获得nan
  • exp_then_mult_check_zero捕获溢出并返回0如果第二个参数为0,则与天真版本相同(请注意inf * 0 == naninf * positive_value == inf) 。如果存在LFV常数,这是你能做的最好的事情。
  • exp_then_mult_scaling使用有关问题的信息来获取其他两个无法处理的输入结果:如果b很小,我们可以将其乘以e,同时递减{{1}而不改变结果。因此,如果在a之前np.exp(a) < np.inf,则结果适合。 (我知道我可以检查它是否适合一步而不是使用循环,但现在更容易编写。)

所以现在你的情况是,一个不需要LFV的解决方案能够为更多的输入对提供正确的结果。 LFV在这里的唯一优势是使用更少的代码行,同时仍然在一个特定的情况下给出正确的结果。

顺便说一下,我不确定b >= 1的线程安全性。因此,如果您在每个线程中使用不同设置的多个线程中使用它,请先测试它以避免以后头痛。

Bonus factoid:original standard实际上规定你应该能够注册一个陷阱处理程序,它会在溢出时被赋予操作结果除以大数(见7.3节)。这将允许您继续计算,只要您记住该值实际上更大。虽然我想它可能会成为多线程环境中WTF的雷区,但不要紧,我并没有真正找到对它的支持。

回到&#34;足够好&#34;从上面指出:根据我的理解,IEEE 754被设计为通用格式,几乎可用于任何应用程序。当你说&#34;同样的问题经常出现在许多不同的环境中时,#(或者至少是)显然不足以证明推广标准的合理性。

让我引用Wikipedia article

  

[...]这里讨论的IEEE 754标准的更深奥的功能,例如扩展格式,NaN,无穷大,次正规等等。[...]旨在为数字不复杂的程序员提供安全可靠的默认值,除了专家支持复杂的数值库外。

暂且不谈,在我看来,即使将NaN作为特殊价值也是一个可疑的决定,添加一个LFV并不能让它更容易或更安全的数字简单和#34;,并且不允许专家做任何他们不能做的事。

我想底线是代表理性数字很难。 IEEE 754可以很好地简化许多应用程序。如果你的不是其中之一,那么最后你只需处理这些困难的事情

  • 使用更高精度的浮子(如果可用的话(好吧,这个非常简单),
  • 仔细选择执行顺序,以便您不会在第一时间出现溢出,
  • 如果您知道他们的所有值都非常大,则为您的所有值添加偏移量
  • 使用无法溢出的任意精度表示(除非内存不足),或
  • 我现在无法想到的其他事情。