【问题标题】:Alternative methods of initializing floats to '+inf', '-inf' and 'nan'将浮点数初始化为“+inf”、“-inf”和“nan”的替代方法
【发布时间】:2019-01-09 11:56:12
【问题描述】:

要将浮点常量初始化为 +inf、-inf、nan,我总是使用带字符串调用的 float():

print(float('inf'), float('+inf'), float('-inf'), float('nan'))

打印出来:

[inf, inf, -inf, nan]

1.) Python 中是否存在初始化这些常量的替代方法(不使用字符串调用 float)?

2.) 我可以通过一些数学运算生成这些常量(+/-inf、nan)吗?

例如用于将变量f 设置为+inf,通过编写f = 1.0 / 0.0 之类的内容(显然,这是除以零错误)。

【问题讨论】:

标签: python python-3.x


【解决方案1】:

您可以从math 模块访问这些数学常数:

>>> from math import inf, nan
>>> inf
inf
>>> nan
nan
>>> inf == float('inf')
True

在幕后,在 CPython 实现中,math.infmath.nan 使用与 float('inf')float('nan') 使用的相同技术生成;这两种方法分别调用 API 函数 _Py_dg_infinity_Py_dg_stdnan

【讨论】:

    【解决方案2】:

    不确定这是否是您想要的,但 numpy 已为此内置变量。

    import numpy as np
    
    a = np.inf
    b = -np.inf
    c = np.nan
    
    print(a, b, c)
    [inf, -inf, nan]
    

    【讨论】:

      【解决方案3】:

      从技术上讲,是的,还有其他方法可以初始化这些值,但它们要么不太明显,要么不太方便。

      如果您的平台使用 IEEE 浮点,1 任何溢出的float 算术,除了溢出之外不引发任何其他标志,保证给您inf。这意味着1.0 / 0.0 可能不起作用(Python 会检测到这是除以零),但更简单的1e500 会。2

      拥有inf 后,您只需执行-infinf/inf 即可获得负无穷大和NaN 值。

      但是阅读您的代码的人会像理解float('nan') 一样容易理解1e500 / 1e500 吗?应该不会吧。

      同时,您始终可以执行struct.unpack('>f', b'\x7f\x80\0\0')[0] 之类的操作,它将 IEEE 大端双精度 inf 值的明确定义的位模式解压缩为 float,无论您的 float 是否是覆盖与否。但是你为什么要写(或读)那个呢?3


      但是,如果您使用的是 Python 3.5 或更高版本,则不需要 初始化这些值;你可以使用the constants in the math module:

      print(math.inf, +math.inf, -math.inf, math.nan)
      

      如果您使用的是 Python 2.7 或 3.4 或其他版本,您始终可以定义自己的常量并反复使用它们:

      inf, nan = float('inf'), float('nan')
      
      print(inf, +inf, -inf, nan)
      

      1。从技术上讲,Python 不需要 IEEE 浮点。事实上,它需要的是类似于平台的 C double 的东西——C 不需要是 IEEE 类型,并且只有在这对实现有意义的情况下(例如,Jython 显然会使用相关的Java 类型而不关心用于编译 JVM 的 C 编译器的想法),并且它并没有明确说明像 C 双精度一样的行为意味着什么。然而,float 类型——更不用说math 模块之类的东西——真的不会工作,除非float 是相当接近 IEEE 浮点类型的东西,比如可能是前 IEEE 的 IBM 和 Intel 类型或不完全 IEEE 摩托罗拉兼容类型。此外,截至 2018 年,三个现有 Python 3.x 实现中唯一支持的平台都为您提供 IEEE 754-1985 double 或 IEEE 754-2008 float64。但是,如果这确实是您的代码的潜在问题,您应该检查 sys.float_info 以验证任何相关的假设。

      2。可以想象,某些平台可能会使用 IEEE 754-1985 long double 或 IEEE 754-2008 float128 之类的。如果您对此感到担心,请使用更大的数字。或者说,1e500 ** 1e500 ** 1e500

      3。好吧,如果你特别需要一个安静或信号 NaN,或者一个具有自定义位模式而不是默认的......但任何需要它的人可能已经知道他们需要它。

      【讨论】:

      • math 模块似乎没问题。第二部分呢?我可以通过一些数学运算产生这些常数吗?
      • @AndrejKesely 你可以——例如,1e500inf1e500 / 1e500 在大多数平台上是nan——但我不认为你 应该。
      • @AndrejKesely 更新了答案以进一步了解血腥细节。
      • 我明白了,这真是个兔子洞;)但是你的回答对我来说很累,谢谢。
      • @AndrejKesely 是的,Python 在边缘的定义总是有点松散,所以像这样的问题通常涉及一个兔子洞,你可以选择你想走多远,而不是说,在 Java 中,您可以在其中引用 1800 页的规范。最终,我们只是猜测未来的实施者如何根据参考中其他地方的隐藏假设或过去在 68000 Mac 和 Amiga 或 S/390 之前的 IBM 等平台上的行为来解释规范的未来版本……
      【解决方案4】:

      math.inf 常量是在 python3.5 中引入的,您可以在https://docs.python.org/3/library/math.html#constants 中阅读更多内容:

      math.inf 浮点正无穷大。 (对于负无穷大, 使用 -math.inf.) 等价于 float('inf') 的输出。

      3.5 版中的新功能。

      正如您在下面看到的,它们都是相同的:

      >>> import math
      >>> math.inf == float('inf')
      True
      >>> math.isinf(math.inf)
      True
      >>>
      

      如果您还对如何检查窗帘下的无穷大感兴趣,可以查看Py_IS_INFINITY 宏。

      【讨论】:

        【解决方案5】:

        如果您希望通过数学运算获得这些值:

        import numpy as np
        a = np.array([-1,0,1],dtype='int8')
        a/0
        

        输出:

        array([-inf,  nan,  inf])
        

        这个结果与IEEE 754 floating point behavior一致。

        在 IEEE 754 算术中,当 a 为 a 为负时为正,负无穷大,a = ±0 时为 NaN。

        【讨论】:

          最近更新 更多