gpt4 book ai didi

python - 数字编程语言是否区分 "largest finite number"和 "infinity"?

转载 作者:太空狗 更新时间:2023-10-29 21:00:49 24 4
gpt4 key购买 nike

问题动机:

在我知道的标准数值语言中(例如 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,infinity),但也许这种直觉是错误的。

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

    最佳答案

    所以......我很好奇并四处挖掘了一下。

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

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

    The IEEE 754 model of setting a flag and returning an infinity or a quiet NaN assumes that the user tests the status frequently (or at least appropriately.) Diagnosing what the original problem was requires the user to check all results for exceptional values, which in turn assumes that they are percolated through all operations, so that erroneous data can be flagged. Given these assumptions, everything should work, but unfortunately they are not very realistic.



    该论文还哀叹对浮点异常处理的支持很差,尤其是在 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使用有关问题的信息来获取其他两个无法处理的输入结果:if b很小,我们可以在递减a的同时乘以e不改变结果。所以如果 np.exp(a) < np.inf之前 b >= 1 ,结果符合。 (我知道我可以在一个步骤中检查它是否适合而不是使用循环,但现在编写起来更容易。)

  • 因此,现在您遇到的情况是,不需要 LFV 的解决方案能够为更多输入对提供正确结果,而不是这样。 LFV 在这里的唯一优势是使用更少的代码行,同时在特定情况下仍能给出正确的结果。

    顺便说一句,我不确定 seterr 的线程安全性.因此,如果您在多个线程中使用它并在每个线程中使用不同的设置,请先对其进行测试以避免以后头痛。

    额外的事实: original standard实际上规定你应该能够注册一个陷阱处理程序,它会在溢出时给出除以大数的运算结果(参见第 7.3 节)。这将允许您继续计算,只要您记住该值实际上要大得多。虽然我猜它在多线程环境中可能会成为 WTF 的雷区,但没关系我并没有真正找到它的支持。

    回到上面“足够好”的观点:在我的理解中,IEEE 754 被设计为一种通用格式,几乎可用于任何应用程序。当您说“在许多不同的环境中经常出现相同的问题”时,它(或至少曾经)显然不足以证明夸大标准的合理性。

    让我引自 Wikipedia article :

    [...] the more esoteric features of the IEEE 754 standard discussed here, such as extended formats, NaN, infinities, subnormals etc. [...] are designed to give safe robust defaults for numerically unsophisticated programmers, in addition to supporting sophisticated numerical libraries by experts.



    撇开这一点,在我看来,即使将 NaN 作为特殊值也是一个有点可疑的决定,添加 LFV 并不能真正让“数字不复杂”的人更容易或更安全,并且不允许专家做他们已经做不到的任何事情。

    我想最重要的是,表示有理数很难。 IEEE 754 在简化许多应用程序方面做得非常好。如果你的不是其中之一,最后你只需要通过任何一个来处理困难的事情
  • 如果可用,使用更高精度的浮点数(好吧,这个很简单),
  • 仔细选择执行顺序,以免首先出现溢出,
  • 如果您知道它们都会非常大,则为所有值添加偏移量,
  • 使用不会溢出的任意精度表示(除非内存不足),或
  • 还有什么我现在想不起来了。
  • 关于python - 数字编程语言是否区分 "largest finite number"和 "infinity"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29859509/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com