6

FloatingPointError: invalid value encountered in subtract在一段测试代码中遇到。在代码本身没有进行任何更改的情况下开始引发异常,因此我在理解它时遇到了很多麻烦。

我的问题:是什么导致了invalid value encountered in subtract异常?为什么它在不同的 python+numpy 安装上会有不同的表现?

细节:

此 MWE不会引发FloatingPointError

>>> import numpy as np
>>> np.__version__
 '1.6.1'
>>> x = np.arange(5,dtype='float64')
>>> y = np.ones(5,dtype='float64')
>>> x[2]=np.nan
>>> x-y
# array([ -1.,   0.,  nan,   2.,   3.])

然而,在一段代码的深处,我减去了两个np.float64 ndarray对象,得到了一个浮点异常。导致异常的数组包含一些非常庞大和微小的数字(例如,1e307 和 1e-307)和一些nans,但我没有对这些数字进行任何组合导致我自己进行异常测试。

更令人不安的是,我有大量的 Jenkins 测试运行与许多版本的 numpy、matplotlib、python 和 scipy 完全相同的代码,但没有一个会引发此异常。我在这一点上迷路了 - 我不知道是否有错误,或者如果有,如何追踪它。

如果您非常好奇,有问题的代码是pyspeckit并且测试在test_hr2421.py.

编辑:跟进 - 我认为这个小片段:np.seterr(invalid='raise')在我正在导入的模块中被调用,特别是 pymc,并且拉取请求已经阻止了这个更改。

4

1 回答 1

3

Numpy 具有关于如何处理浮点错误的可配置行为。默认情况下,下溢错误会被忽略,而其他错误则会触发警告。对于每个类别,用户可以使用 更改此行为numpy.seterr。这些设置是全局的——这里没有命名空间;所以如果一个库调用numpy.seterr(all='raise'),那么这将影响整个程序,直到numpy.seterr再次调用。

您可以确认这确实是您的问题的原因

print(numpy.seterr())

应该输出类似

{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}

如果其中一些类别具有 value raise,尤其是 key 'invalid',那么这将解释您正在观察的行为。

numpy.seterr(invalid='warn')您可以通过调用或来抑制此异常invalid='ignore'。有关可能的错误的完整列表,请通读numpy.seterr.

您还可以使用上下文管理器临时更改行为:

In [12]: x = np.arange(-5, 5,dtype='float64')

In [13]: with np.errstate(divide="raise"):
    print(1/x)
   ....:     
---------------------------------------------------------------------------
FloatingPointError                        Traceback (most recent call last)
<ipython-input-13-881589fdcb7a> in <module>()
      1 with np.errstate(divide="raise"):
----> 2     print(1/x)
      3 

FloatingPointError: divide by zero encountered in true_divide

In [14]: with np.errstate(divide="warn"):
    print(1/x)
   ....:     
/home/users/gholl/venv/stable-3.5/bin/ipython3:2: RuntimeWarning: divide by zero encountered in true_divide
  
[-0.2        -0.25       -0.33333333 -0.5        -1.                 inf
  1.          0.5         0.33333333  0.25      ]

In [15]: with np.errstate(divide="ignore"):
    print(1/x)
   ....:     
[-0.2        -0.25       -0.33333333 -0.5        -1.                 inf
  1.          0.5         0.33333333  0.25      ]

我倾向于将我的整个代码包装在一个with np.errstate(all="raise")块中,然后使用上下文管理器忽略特定条件,如果我确定问题不是隐藏错误 - 但通常是这样。

如果确实有一个库永久更改状态,我会向维护者提出问题或发送拉取请求,因为他们确实应该使用上下文管理器,因此他们的更改设置仅适用于他们的代码块,而不适用于程序的其余部分。

于 2014-08-22T19:58:19.500 回答