浮点数陷阱
精度和准确性
对于某些操作,Numba 可能会使用与 Python 或 Numpy 不同的算法。结果可能不会逐位兼容。差异通常应该很小,并在合理预期之内。然而,小的累积差异最终可能会产生大的差异,尤其是在涉及发散函数的情况下。
数学库实现
Numba 支持多种平台和操作系统,每个平台和操作系统都有自己的数学库实现(在此称为 libm
)。libm
中包含的大多数数学函数都有 IEEE 754 标准规定的特定要求(例如 sin()
、exp()
等),但每个实现都可能存在错误。因此,在某些平台上,Numba 必须特别小心,以解决已知的 libm
问题。
另一个典型问题是,当操作系统的 libm
函数集不完整,需要补充额外函数时。这些函数是参照 IEEE 754 和 C99 标准提供的,并且在 Numba 中的实现方式通常与等效的 CPython 函数类似。
线性代数
即使输入为 float32
,Numpy 也会强制某些线性代数操作以双精度模式运行。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)