Python:大型浮动如何自动变成*inf*?

Joh*_*alt 11 python

在Python 3.6.2,Win10-x64上

我遇到过一些奇怪的事情并且无法解释.

在:

x = 10.0
for i in range(10):
    print(str(i) + " | " + str(x))
    x *= x
Run Code Online (Sandbox Code Playgroud)

日期:

0 | 10.0
1 | 100.0
2 | 10000.0
3 | 100000000.0
4 | 1e+16
5 | 1e+32
6 | 1.0000000000000002e+64
7 | 1.0000000000000003e+128
8 | 1.0000000000000005e+256
9 | inf
Run Code Online (Sandbox Code Playgroud)

它怎么变成了inf为什么不抛出异常?

如果我用例如*=最后一行替换**=它,那么通过第二次迭代它会升起OverflowError: (34, 'Result too large'),这是有意义的(因为它仅为10.000.000.000 ^ 10.000.000.000).

这是否意味着浮子有一种"软"限制 - 当超过时 - 将它们变成inf?如果是这样,那么限制是什么,无论算术运算如何都是一样的?这不意味着什么inf == this_limit + anything

.

加:

我明白了sys.float_info.max.那是极限吗?

我刚才有了一个想法并测试了一些东西:

print(sys.float_info.max)
print(sys.float_info.max + 1)
print(sys.float_info.max * 2)
print(sys.float_info.max * 1.000000000000001)
print(sys.float_info.max * 1.0000000000000001)
Run Code Online (Sandbox Code Playgroud)

这给了我:

1.7976931348623157e+308
1.7976931348623157e+308
inf
inf
1.7976931348623157e+308
Run Code Online (Sandbox Code Playgroud)

这对我来说很奇怪......

use*_*ica 6

怎么变成inf呢?

因为即使是最大的有限浮点数,结果也太大了,所以它溢出了.

为什么不抛出异常?

因为Python在将inf/nan变成异常的时候并不是很一致.一些操作提供例外.有些给inf/nan.

这是否意味着浮子有一种"软"限制 - 当超过时 - 将它们变成inf?如果是这样,那么限制是什么,无论算术运算如何都是一样的?并不意味着inf == this_limit +任何东西?

舍入后任何大于1.7976931348623157e + 308的结果都会成为inf(或者如果Python感觉像是异常).确切的限制不能表示为浮动; 你无法实际执行this_limit + anything,因为试图放入this_limit浮动将其舍入为1.7976931348623157e + 308.

  • @randomir:是的,`float_pow`前一段时间是[固定](https://bugs.python.org/issue7534),以便更好地遵循IEEE 754.理论上,相同类型的修复可以应用于所有算术运算,但是你会遇到故意使用`1e300*1e300`(例如)作为生成IEEE 754无穷大的方法的旧代码. (2认同)

ran*_*mir 6

转到源代码

如果您检查 CPython () 中的浮点运算实现floatobject.c,您会发现大多数操作都简单地推迟到 Cdouble操作,例如在float_addfloat_sub或 中float_mul

static PyObject *
float_mul(PyObject *v, PyObject *w)
{
    double a,b;
    CONVERT_TO_DOUBLE(v, a);
    CONVERT_TO_DOUBLE(w, b);
    PyFPE_START_PROTECT("multiply", return 0)
    a = a * b;
    PyFPE_END_PROTECT(a)
    return PyFloat_FromDouble(a);
}
Run Code Online (Sandbox Code Playgroud)

结果a = a * b是根据 IEEE 754 标准在 CPU 上计算的,该标准规定了在溢出情况下(即当结果无法存储在有限的 32 位或 64 位中时)的默认结果-inf/ 。+inffloatdouble

另一件需要注意的事情是,IEEE 754 中定义的五个浮点异常(如溢出)中的任何一个都可以 (1) 生成默认值并将异常信息存储在状态字中或 (2)SIGFPE如果 FP 则引发信号异常陷阱已启用(请参阅有关 FP 异常的 GNU lib C 文档)。

在Python中,陷阱永远不会启用,因此程序以不间断的模式运行。

乘法溢出

这意味着,float_mul上述例程中的溢出结果将默认为 float inf(如 IEEE 754 中定义),并且 Python 将简单地返回PyFloat_FromDouble(a),其中ainf

求幂溢出

另一方面,如果我们检查float_pow(下面的缩短版本):

static PyObject *
float_pow(PyObject *v, PyObject *w, PyObject *z)
{
    ...
    errno = 0;
    PyFPE_START_PROTECT("pow", return NULL)
    ix = pow(iv, iw);
    PyFPE_END_PROTECT(ix)
    Py_ADJUST_ERANGE1(ix);

    if (errno != 0) {
        PyErr_SetFromErrno(errno == ERANGE ? PyExc_OverflowError :
                             PyExc_ValueError);
        ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

我们可以看到你的结果x **= x inf,如果没有额外的状态字(~ )检查——并且在底层溢出时errno引发Python异常。OverflowErrorpow


总之(正如这里已经指出的),Python 在处理浮点异常方面并不一致——有时会返回默认值(来自 C// libcCPU),有时会引发 Python 异常。