浮点陷阱

精度和准确度

对于某些操作,Numba 可能会使用与 Python 或 Numpy 不同的算法。结果可能不会逐位兼容。差异通常应该很小,并且在合理预期范围内。然而,小的累积差异可能会在最后产生大的差异,特别是在涉及发散函数的情况下。

数学库实现

Numba 支持多种平台和操作系统,每个平台都有其自己的数学库实现(从此处称为 libm)。libm 中包含的大多数数学函数都有 IEEE 754 标准规定的具体要求(如 sin()exp() 等),但每个实现可能都存在错误。因此,在某些平台上,Numba 必须特别小心,以绕过已知的 libm 问题。

另一个典型问题是当操作系统的 libm 函数集不完整,需要通过额外函数来补充。这些函数参照 IEEE 754 和 C99 标准提供,通常在 Numba 中以类似于 CPython 等效函数的方式实现。

线性代数

Numpy 强制某些线性代数操作以双精度模式运行,即使输入是 float32。Numba 将始终观察输入的精度,并在所有输入为 float32complex64 时调用单精度线性代数例程。

Numba 中 numpy.linalg 例程的实现仅支持在提供底层核心功能的 LAPACK 函数中使用的浮点类型。因此,仅支持 float32float64complex64complex128 类型。如果用户例如有一个 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)