NumPy 核心数学库#
NumPy 核心数学库 (npymath
) 是朝着这个方向迈出的第一步。该库包含了大多数与数学相关的 C99 功能,可在 C99 支持不佳的平台上使用。核心数学函数与 C99 函数具有相同的 API,只是前缀为 npy_*
。
可用函数定义在 <numpy/npy_math.h>
中 —— 如有疑问,请参考此头文件。
注意
目前正在努力使 npymath
更小(因为编译器的 C99 兼容性随时间推移已有所改善),并更容易作为仅头文件依赖项进行分发或使用。这将避免与下游包或最终用户使用的编译器可能不匹配的静态库分发问题。有关详细信息,请参阅 gh-20880。
浮点数分类#
-
NPY_NAN#
此宏定义为 NaN(非数字),并保证其符号位未设置(“正”NaN)。对应的单精度和扩展精度宏分别带有 F 和 L 后缀。
-
NPY_INFINITY#
此宏定义为正无穷大。对应的单精度和扩展精度宏分别带有 F 和 L 后缀。
-
NPY_PZERO#
此宏定义为正零。对应的单精度和扩展精度宏分别带有 F 和 L 后缀。
-
NPY_NZERO#
此宏定义为负零(即符号位已设置)。对应的单精度和扩展精度宏分别带有 F 和 L 后缀。
-
npy_isnan(x)#
这是 C99
isnan
的别名:适用于单精度、双精度和扩展精度,如果 `x` 是 NaN 则返回非零值。
-
npy_isfinite(x)#
这是 C99
isfinite
的别名:适用于单精度、双精度和扩展精度,如果 `x` 既不是 NaN 也不是无穷大则返回非零值。
-
npy_isinf(x)#
这是 C99
isinf
的别名:适用于单精度、双精度和扩展精度,如果 `x` 是无穷大(正无穷和负无穷)则返回非零值。
-
npy_signbit(x)#
这是 C99
signbit
的别名:适用于单精度、双精度和扩展精度,如果 `x` 的符号位已设置(即数字为负)则返回非零值。
-
npy_copysign(x, y)#
这是 C99
copysign
的别名:返回具有与 `y` 相同符号的 `x`。适用于任何值,包括无穷大和 NaN。单精度和扩展精度分别带有 `f` 和 `l` 后缀。
有用的数学常量#
以下数学常量在 npy_math.h
中可用。通过分别添加 f
和 l
后缀,也可以获得单精度和扩展精度版本。
-
NPY_E#
自然对数的底数 (\(e\))
-
NPY_LOG2E#
欧拉常数的以 2 为底的对数 (\(\frac{\ln(e)}{\ln(2)}\))
-
NPY_LOG10E#
欧拉常数的以 10 为底的对数 (\(\frac{\ln(e)}{\ln(10)}\))
-
NPY_LOGE2#
2 的自然对数 (\(\ln(2)\))
-
NPY_LOGE10#
10 的自然对数 (\(\ln(10)\))
-
NPY_PI#
圆周率 (\(\pi\))
-
NPY_PI_2#
圆周率除以 2 (\(\frac{\pi}{2}\))
-
NPY_PI_4#
圆周率除以 4 (\(\frac{\pi}{4}\))
-
NPY_1_PI#
圆周率的倒数 (\(\frac{1}{\pi}\))
-
NPY_2_PI#
圆周率倒数的两倍 (\(\frac{2}{\pi}\))
-
NPY_EULER#
- 欧拉常数
\(\lim_{n\rightarrow\infty}({\sum_{k=1}^n{\frac{1}{k}}-\ln n})\)
底层浮点操作#
这些对于精确的浮点数比较可能很有用。
-
double npy_nextafter(double x, double y)#
这是 C99
nextafter
的别名:返回从 `x` 到 `y` 方向上的下一个可表示浮点值。单精度和扩展精度分别带有 `f` 和 `l` 后缀。
-
double npy_spacing(double x)#
此函数等同于 Fortran 内置函数。返回 `x` 与从 `x` 开始的下一个可表示浮点值之间的距离,例如 `spacing(1) == eps`。NaN 和 +/- inf 的 `spacing` 返回 NaN。单精度和扩展精度分别带有 `f` 和 `l` 后缀。
-
void npy_set_floatstatus_divbyzero()#
设置除以零浮点异常
-
void npy_set_floatstatus_overflow()#
设置溢出浮点异常
-
void npy_set_floatstatus_underflow()#
设置下溢浮点异常
-
void npy_set_floatstatus_invalid()#
设置无效浮点异常
-
int npy_get_floatstatus()#
获取浮点状态。返回一个包含以下可能标志的位掩码
NPY_FPE_DIVIDEBYZERO
NPY_FPE_OVERFLOW
NPY_FPE_UNDERFLOW
NPY_FPE_INVALID
请注意,
npy_get_floatstatus_barrier
更受推荐,因为它能防止激进的编译器优化重新排序相对于设置状态的代码的调用,从而避免不正确的结果。
-
int npy_get_floatstatus_barrier(char*)#
获取浮点状态。传入指向局部变量的指针,以防止激进的编译器优化重新排序相对于设置状态的代码的此函数调用,从而避免不正确的结果。
返回一个包含以下可能标志的位掩码
NPY_FPE_DIVIDEBYZERO
NPY_FPE_OVERFLOW
NPY_FPE_UNDERFLOW
NPY_FPE_INVALID
-
int npy_clear_floatstatus()#
清除浮点状态。返回先前的状态掩码。
请注意,
npy_clear_floatstatus_barrier
更受推荐,因为它能防止激进的编译器优化重新排序相对于设置状态的代码的调用,从而避免不正确的结果。
-
int npy_clear_floatstatus_barrier(char*)#
清除浮点状态。传入指向局部变量的指针,以防止激进的编译器优化重新排序此函数调用。返回先前的状态掩码。
复数支持#
已添加类似 C99 的复数函数。如果您希望实现可移植的 C 扩展,可以使用这些函数。自 NumPy 2.0 起,我们使用 C99 复数类型作为底层类型
typedef double _Complex npy_cdouble;
typedef float _Complex npy_cfloat;
typedef long double _Complex npy_clongdouble;
MSVC 本身不支持 _Complex
类型,但通过提供自己的实现,已添加对 C99 complex.h
头文件的支持。因此,在 MSVC 下,将使用等效的 MSVC 类型
typedef _Dcomplex npy_cdouble;
typedef _Fcomplex npy_cfloat;
typedef _Lcomplex npy_clongdouble;
由于 MSVC 仍不支持用于初始化复数的 C99 语法,因此您需要限制为 C90 兼容的语法,例如
/* a = 1 + 2i \*/
npy_complex a = npy_cpack(1, 2);
npy_complex b;
b = npy_log(a);
numpy/npy_math.h
中也添加了一些实用工具,用于获取或设置复数的实部或虚部
npy_cdouble c;
npy_csetreal(&c, 1.0);
npy_csetimag(&c, 0.0);
printf("%d + %di\n", npy_creal(c), npy_cimag(c));
2.0.0 版本中的更改:所有 NumPy 复数类型的底层 C 类型已更改为使用 C99 复数类型。在此之前,以下结构用于表示复数类型
typedef struct { double real, imag; } npy_cdouble;
typedef struct { float real, imag; } npy_cfloat;
typedef struct {npy_longdouble real, imag;} npy_clongdouble;
使用 struct
表示确保了复数可以在所有平台上使用,即使是没有内置复数类型支持的平台。这也意味着必须随 NumPy 一起分发一个静态库,以为下游包提供 C99 兼容层。然而,近年来,对原生复数类型的支持已大大改善,MSVC 在 2019 年添加了对 complex.h
头文件的内置支持。
为了简化跨版本兼容性,已添加使用新 API 的宏。
#define NPY_CSETREAL(z, r) npy_csetreal(z, r)
#define NPY_CSETIMAG(z, i) npy_csetimag(z, i)
numpy/npy_2_complexcompat.h
中也提供了一个兼容层。它检查宏是否存在,如果不存在则回退到 1.x 语法。
#include <numpy/npy_math.h>
#ifndef NPY_CSETREALF
#define NPY_CSETREALF(c, r) (c)->real = (r)
#endif
#ifndef NPY_CSETIMAGF
#define NPY_CSETIMAGF(c, i) (c)->imag = (i)
#endif
我们建议所有需要此功能的下游包将其兼容层代码复制粘贴到自己的源代码中并使用,以便它们可以继续无问题地支持 NumPy 1.x 和 2.x。另请注意,complex.h
头文件包含在 numpy/npy_common.h
中,这使得 complex
成为保留关键字。
在扩展中链接核心数学库#
要在您自己的 Python 扩展中使用 NumPy 作为静态库提供的核心数学库,您需要将 npymath
编译和链接选项添加到您的扩展中。具体步骤将取决于您使用的构建系统。通用步骤如下:
将 NumPy 的包含目录(=
np.get_include()
的值)添加到您的包含目录中,npymath
静态库位于 NumPy 包含目录旁边的lib
目录中(即pathlib.Path(np.get_include()) / '..' / 'lib'
)。将其添加到您的库搜索目录中,链接
libnpymath
和libm
。
注意
请记住,当您进行交叉编译时,必须使用您正在构建的平台的 NumPy,而不是构建机器的原生 NumPy。否则,您将使用为错误架构构建的静态库。
当您使用 numpy.distutils
(已弃用) 构建时,请在您的 setup.py
中使用此项
>>> from numpy.distutils.misc_util import get_info >>> info = get_info('npymath') >>> _ = config.add_extension('foo', sources=['foo.c'], extra_info=info)
换句话说,info
的用法与使用 blas_info
等完全相同。
当您使用 Meson 构建时,请使用
# Note that this will get easier in the future, when Meson has
# support for numpy built in; most of this can then be replaced
# by `dependency('numpy')`.
incdir_numpy = run_command(py3,
[
'-c',
'import os; os.chdir(".."); import numpy; print(numpy.get_include())'
],
check: true
).stdout().strip()
inc_np = include_directories(incdir_numpy)
cc = meson.get_compiler('c')
npymath_path = incdir_numpy / '..' / 'lib'
npymath_lib = cc.find_library('npymath', dirs: npymath_path)
py3.extension_module('module_name',
...
include_directories: inc_np,
dependencies: [npymath_lib],
半精度函数#
头文件 <numpy/halffloat.h>
提供了处理 IEEE 754-2008 16 位浮点值的函数。虽然这种格式通常不用于数值计算,但它对于存储需要浮点但不要求高精度的值很有用。它也可以作为理解浮点舍入误差性质的教育工具。
与其他类型一样,NumPy 包含一个用于 16 位浮点数的 typedef npy_half
。但与大多数其他类型不同,您不能在 C 语言中将其作为普通类型使用,因为它是一个 npy_uint16
的 typedef。例如,1.0 在 C 中看起来像 0x3c00,如果您对不同的带符号零进行相等比较,您将得到 -0.0 != 0.0 (0x8000 != 0x0000),这是不正确的。
出于这些原因,NumPy 提供了一个通过包含 <numpy/halffloat.h>
并链接到 npymath
来使用 npy_half
值的 API。对于未直接提供的函数,例如算术运算,首选方法是将其转换为 `float` 或 `double`,然后再转换回来,如下例所示。
npy_half sum(int n, npy_half *array) {
float ret = 0;
while(n--) {
ret += npy_half_to_float(*array++);
}
return npy_float_to_half(ret);
}
外部链接
-
NPY_HALF_ZERO#
此宏定义为正零。
-
NPY_HALF_PZERO#
此宏定义为正零。
-
NPY_HALF_NZERO#
此宏定义为负零。
-
NPY_HALF_ONE#
此宏定义为 1.0。
-
NPY_HALF_NEGONE#
此宏定义为 -1.0。
-
NPY_HALF_PINF#
此宏定义为 +inf。
-
NPY_HALF_NINF#
此宏定义为 -inf。
-
NPY_HALF_NAN#
此宏定义为 NaN 值,并保证其符号位未设置。
-
npy_half npy_float_to_half(float f)#
将单精度浮点数转换为半精度浮点数。该值将四舍五入到最近的可表示半精度值,遇平局时四舍五入到最近的偶数。如果该值过小或过大,则会设置系统的浮点下溢或溢出位。
-
npy_half npy_double_to_half(double d)#
将双精度浮点数转换为半精度浮点数。该值将四舍五入到最近的可表示半精度值,遇平局时四舍五入到最近的偶数。如果该值过小或过大,则会设置系统的浮点下溢或溢出位。
-
npy_half npy_half_nextafter(npy_half x, npy_half y)#
这与底层浮点部分描述的 `npy_nextafter` 和 `npy_nextafterf` 对半精度浮点数的作用相同。
-
npy_uint16 npy_floatbits_to_halfbits(npy_uint32 f)#
底层函数,将存储为 `uint32` 的 32 位单精度浮点数转换为 16 位半精度浮点数。
-
npy_uint16 npy_doublebits_to_halfbits(npy_uint64 d)#
底层函数,将存储为 `uint64` 的 64 位双精度浮点数转换为 16 位半精度浮点数。
-
npy_uint32 npy_halfbits_to_floatbits(npy_uint16 h)#
底层函数,将 16 位半精度浮点数转换为存储为 `uint32` 的 32 位单精度浮点数。
-
npy_uint64 npy_halfbits_to_doublebits(npy_uint16 h)#
底层函数,将 16 位半精度浮点数转换为存储为 `uint64` 的 64 位双精度浮点数。