浮点陷阱
精度和准确度
对于某些操作,Numba 可能会使用与 Python 或 Numpy 不同的算法。结果可能不会逐位兼容。差异通常应该很小,并且在合理预期范围内。然而,小的累积差异可能会在最后产生大的差异,特别是在涉及发散函数的情况下。
数学库实现
Numba 支持多种平台和操作系统,每个平台都有其自己的数学库实现(从此处称为 libm
)。libm
中包含的大多数数学函数都有 IEEE 754 标准规定的具体要求(如 sin()
、exp()
等),但每个实现可能都存在错误。因此,在某些平台上,Numba 必须特别小心,以绕过已知的 libm
问题。
另一个典型问题是当操作系统的 libm
函数集不完整,需要通过额外函数来补充。这些函数参照 IEEE 754 和 C99 标准提供,通常在 Numba 中以类似于 CPython 等效函数的方式实现。
线性代数
Numpy 强制某些线性代数操作以双精度模式运行,即使输入是 float32
。Numba 将始终观察输入的精度,并在所有输入为 float32
或 complex64
时调用单精度线性代数例程。
Numba 中 numpy.linalg
例程的实现仅支持在提供底层核心功能的 LAPACK 函数中使用的浮点类型。因此,仅支持 float32
、float64
、complex64
和 complex128
类型。如果用户例如有一个 int32
类型,则必须在这些例程中使用之前将其转换为适当的浮点类型。这一决定的原因是为了避免必须复制 Numpy 中做出的类型转换选择,并鼓励用户为其正在进行的操作选择最佳的浮点类型。
混合类型操作
Numpy 在计算混合整数和浮点操作数时,通常会返回一个 float64
作为结果(一个典型的例子是幂运算符 **
)。相比之下,Numba 会选择浮点操作数中最高精度的类型,因此例如 float32 ** int32
将返回一个 float32
,无论输入值如何。这使得性能特征更容易预测,但如果你需要额外的精度,你应该显式地将输入转换为 float64
。
警告和错误
当调用使用 vectorize()
创建的 ufunc 时,Numpy 会通过检查 FPU 错误字来确定是否发生了错误。然后,根据当前的错误处理设置,它可能会打印出警告或引发异常(例如 RuntimeWarning: divide by zero encountered
)。
然而,根据LLVM如何优化ufunc的代码,可能会出现一些虚假的警告或错误。如果你遇到这个问题,我们建议你调用 numpy.seterr()
来更改Numpy的错误处理设置,或者使用 numpy.errstate
上下文管理器来暂时切换它们:
with np.errstate(all='ignore'):
x = my_ufunc(y)