NumPy 源码解析(七十三)
.\numpy\numpy\_core\src\multiarray\textreading\tokenize.h
extern "C" {
// 枚举定义不同的解析状态
typedef enum {
/* Initialization of fields */
TOKENIZE_INIT, // 初始化字段
TOKENIZE_CHECK_QUOTED, // 检查引号状态
/* Main field parsing states */
TOKENIZE_UNQUOTED, // 未引用字段解析状态
TOKENIZE_UNQUOTED_WHITESPACE,
.\numpy\numpy\_core\src\multiarray\usertypes.c
/*
提供多维数组作为 Python 中的基本对象类型。
基于原始的 Numeric 实现
版权所有 (c) 1995, 1996, 1997 Jim Hugunin, hugunin@mit.edu
同时也包含了许多 Numeric Python 开发者在 1995-2004 年间的贡献
2005 年大幅修改,受 Numarray 启发
作者:Travis Oliphant, oliphant@ee.byu.edu
所属:Brigham Young University
维护者邮箱:oliphant.travis@ieee.org
Numarray 的设计思路(提供了指导)来自于
Space Science Telescope Institute
(J. Todd Miller, Perry Greenfield, Rick White)
*/
/*
NPY_NO_EXPORT 表示此函数不会导出到公共 API
userdescrs 用于保存用户定义的数据描述符
*/
NPY_NO_EXPORT _PyArray_LegacyDescr **userdescrs = NULL;
/*
功能:向整数数组添加新元素
参数:p_types - 指向整数数组的指针
insert - 要插入的整数值
返回值:成功返回 0,内存分配失败返回 -1
*/
static int
_append_new(int **p_types, int insert)
{
int n = 0;
int *newtypes;
int *types = *p_types;
// 找到数组中的最后一个元素
while (types[n] != NPY_NOTYPE) {
n++;
}
// 重新分配内存,以添加新元素
newtypes = (int *)realloc(types, (n + 2)*sizeof(int));
if (newtypes == NULL) {
PyErr_NoMemory(); // 内存分配失败,抛出内存错误异常
return -1;
}
newtypes[n] = insert; // 在数组末尾添加新元素
newtypes[n + 1] = NPY_NOTYPE;
/* 替换传入的指针 */
*p_types = newtypes;
return 0;
}
/*
默认的非零判断函数
功能:检查数组中的元素是否存在非零值
参数:ip - 数组元素的指针
arr - 数组对象的指针
返回值:存在非零元素返回 NPY_TRUE,否则返回 NPY_FALSE
*/
static npy_bool
_default_nonzero(void *ip, void *arr)
{
int elsize = PyArray_ITEMSIZE(arr); // 获取数组元素的大小
char *ptr = ip;
while (elsize--) {
if (*ptr++ != 0) { // 检查元素是否为非零
return NPY_TRUE;
}
}
return NPY_FALSE;
}
/*
默认的复制和交换函数
功能:将源数组的部分内容复制到目标数组,支持交换字节序
参数:dst - 目标数组的起始地址
dstride - 目标数组中相邻元素之间的字节偏移量
src - 源数组的起始地址
sstride - 源数组中相邻元素之间的字节偏移量
n - 要复制的元素个数
swap - 是否交换字节序的标志
arr - 数组对象的指针
*/
static void
_default_copyswapn(void *dst, npy_intp dstride, void *src,
npy_intp sstride, npy_intp n, int swap, void *arr)
{
npy_intp i;
PyArray_CopySwapFunc *copyswap;
char *dstptr = dst;
char *srcptr = src;
copyswap = PyDataType_GetArrFuncs(PyArray_DESCR(arr))->copyswap; // 获取复制和交换函数
for (i = 0; i < n; i++) {
copyswap(dstptr, srcptr, swap, arr); // 调用复制和交换函数
dstptr += dstride; // 移动到下一个目标元素的位置
srcptr += sstride; // 移动到下一个源元素的位置
}
}
/*NUMPY_API
初始化 arrfuncs 结构体,将所有函数指针初始化为 NULL
*/
NPY_NO_EXPORT void
PyArray_InitArrFuncs(PyArray_ArrFuncs *f)
{
int i;
for(i = 0; i < NPY_NTYPES_ABI_COMPATIBLE; i++) {
f->cast[i] = NULL; // 将类型转换函数指针数组初始化为 NULL
}
f->getitem = NULL; // 获取元素函数指针初始化为 NULL
f->setitem = NULL; // 设置元素函数指针初始化为 NULL
f->copyswapn = NULL; // 复制和交换多个元素函数指针初始化为 NULL
f->copyswap = NULL; // 复制和交换单个元素函数指针初始化为 NULL
f->compare = NULL; // 比较函数指针初始化为 NULL
f->argmax = NULL; // 最大值索引函数指针初始化为 NULL
f->argmin = NULL; // 最小值索引函数指针初始化为 NULL
f->dotfunc = NULL; // 点积函数指针初始化为 NULL
f->scanfunc = NULL; // 扫描函数指针初始化为 NULL
f->fromstr = NULL; // 字符串转数组函数指针初始化为 NULL
f->nonzero = NULL; // 非零判断函数指针初始化为 NULL
f->fill = NULL; // 填充函数指针初始化为 NULL
f->fillwithscalar = NULL; // 使用标量填充函数指针初始化为 NULL
for(i = 0; i < NPY_NSORTS; i++) {
f->sort[i] = NULL; // 排序函数指针数组初始化为 NULL
f->argsort[i] = NULL; // 获取排序索引函数指针数组初始化为 NULL
}
f->castdict = NULL; // 类型转换字典指针初始化为 NULL
f->scalarkind = NULL; // 标量类型指针初始化为 NULL
f->cancastscalarkindto = NULL; // 标量类型转换函数指针初始化为 NULL
f->cancastto = NULL; // 类型转换函数指针初始化为 NULL
f->_unused1 = NULL; // 未使用的字段1初始化为 NULL
f->_unused2 = NULL; // 未使用的字段2初始化为 NULL
}
f->_unused3 = NULL;
// 将结构体指针 f 的成员变量 _unused3 设置为 NULL
/*
返回与此类型相关联的类型编号 >=NPY_USERDEF。
需要arraytypes.inc中定义的userdecrs表和PyArray_NUMUSER变量。
*/
/*NUMPY_API
* 注册数据类型
*
* 从原型创建一个新的描述符。
*
* 该原型与NumPy 1.x ABI兼容,在1.x中将用作实际描述符。但由于ABI已更改,这在2.0中无法工作,
* 我们将所有字段复制到新的结构体中。
*
* 成功注册后,代码必须使用`descr = PyArray_DescrFromType(num);`。这与1.x中的使用兼容。
*
* 此函数仅在2.x中复制所有内部引用。这应该是不相关的,因为任何内部引用都是不朽的。
*/
NPY_NO_EXPORT int
PyArray_RegisterDataType(PyArray_DescrProto *descr_proto)
{
int typenum;
int i;
PyArray_ArrFuncs *f;
/* 查看是否已经注册了此类型 */
for (i = 0; i < NPY_NUMUSERTYPES; i++) {
if (userdescrs[i]->type_num == descr_proto->type_num) {
return descr_proto->type_num;
}
}
typenum = NPY_USERDEF + NPY_NUMUSERTYPES;
if (typenum >= NPY_VSTRING) {
PyErr_SetString(PyExc_ValueError,
"Too many user defined dtypes registered");
return -1;
}
descr_proto->type_num = -1;
if (PyDataType_ISUNSIZED(descr_proto)) {
PyErr_SetString(PyExc_ValueError, "cannot register a" \
"flexible data-type");
return -1;
}
f = descr_proto->f;
if (f->nonzero == NULL) {
f->nonzero = _default_nonzero;
}
if (f->copyswapn == NULL) {
f->copyswapn = _default_copyswapn;
}
if (f->copyswap == NULL || f->getitem == NULL ||
f->setitem == NULL) {
PyErr_SetString(PyExc_ValueError, "a required array function" \
" is missing.");
return -1;
}
if (descr_proto->typeobj == NULL) {
PyErr_SetString(PyExc_ValueError, "missing typeobject");
return -1;
}
int use_void_clearimpl = 0;
/*
* 如果描述符的标志包含 NPY_ITEM_IS_POINTER 或 NPY_ITEM_REFCOUNT 标志位,
* 表明用户自定义的数据类型实际上不能执行引用计数。然而,存在某些已有的
* 手段(例如 xpress),它们使用了结构化的方法:
* dtype((xpress.var, [('variable', 'O')]))
* 因此我们必须支持这种情况。但这样的结构必须是常量的(即在注册时固定,
* 这是对于 `xpress` 而言的情况)。
*/
use_void_clearimpl = 1;
/*
* 如果描述符的 names 或 fields 为 NULL,或者 fields 不是 PyDict 类型,
* 则抛出错误。这是因为不支持使用 `NPY_ITEM_IS_POINTER` 或 `NPY_ITEM_REFCOUNT`
* 的旧用户数据类型。只有在注册时硬编码了 names 和 fields 的结构化数据类型才能被创建。
*/
if (descr_proto->names == NULL || descr_proto->fields == NULL ||
!PyDict_CheckExact(descr_proto->fields)) {
PyErr_Format(PyExc_ValueError,
"Failed to register dtype for %S: Legacy user dtypes "
"using `NPY_ITEM_IS_POINTER` or `NPY_ITEM_REFCOUNT` are "
"unsupported. It is possible to create such a dtype only "
"if it is a structured dtype with names and fields "
"hardcoded at registration time.\n"
"Please contact the NumPy developers if this used to work "
"but now fails.", descr_proto->typeobj);
return -1;
}
/*
* 扩展 userdescrs 数组,以便容纳新的用户数据类型描述符。
* 如果内存分配失败,则返回内存错误。
*/
userdescrs = realloc(userdescrs,
(NPY_NUMUSERTYPES+1)*sizeof(void *));
if (userdescrs == NULL) {
PyErr_SetString(PyExc_MemoryError, "RegisterDataType");
return -1;
}
/*
* 由于旧用户数据类型类无法具有名称(因为用户从未定义过名称),
* 我们在这里为其创建一个名称。这些数据类型在本质上是静态类型。
*
* 注意:我们没有意图再次释放这段内存,因为这与静态类型定义的行为完全一致。
*/
const char *scalar_name = descr_proto->typeobj->tp_name;
/*
* 为了获得一个合理的 __name__,我们必须仅获取名称,并忽略模块部分,
* 这是因为静态类型在这方面受到限制(尽管这不是理想的,但在实践中并不是一个大问题)。
* 这就是 Python 用于打印静态类型 __name__ 的方法。
*/
const char *dot = strrchr(scalar_name, '.');
if (dot) {
scalar_name = dot + 1;
}
Py_ssize_t name_length = strlen(scalar_name) + 14;
/*
* 分配内存并格式化名称字符串,格式为 "numpy.dtype[类型名]"。
* 如果内存分配失败,则返回内存错误。
*/
char *name = PyMem_Malloc(name_length);
if (name == NULL) {
PyErr_NoMemory();
return -1;
}
snprintf(name, name_length, "numpy.dtype[%s]", scalar_name);
/*
* 将用户提供的描述符结构体复制到一个新的结构体中。这样做是为了允许两者之间的布局不同。
* 如果内存分配失败,则释放已分配的名称内存并返回内存错误。
*/
_PyArray_LegacyDescr *descr = PyObject_Malloc(sizeof(_PyArray_LegacyDescr));
if (descr == NULL) {
PyMem_FREE(name);
PyErr_NoMemory();
return -1;
}
PyObject_INIT(descr, Py_TYPE(descr_proto));
/*
* 简单地按名称复制所有字段:
* 增加类型对象的引用计数,设置类型对象和类型的种类。
*/
Py_XINCREF(descr_proto->typeobj);
descr->typeobj = descr_proto->typeobj;
descr->kind = descr_proto->kind;
// 将描述符的类型设置为原型描述符的类型
descr->type = descr_proto->type;
// 将描述符的字节顺序设置为原型描述符的字节顺序
descr->byteorder = descr_proto->byteorder;
// 将描述符的标志设置为原型描述符的标志
descr->flags = descr_proto->flags;
// 将描述符的元素大小设置为原型描述符的元素大小
descr->elsize = descr_proto->elsize;
// 将描述符的对齐方式设置为原型描述符的对齐方式
descr->alignment = descr_proto->alignment;
// 将描述符的子数组设置为原型描述符的子数组
descr->subarray = descr_proto->subarray;
// 增加原型描述符的字段的引用计数并将其赋给描述符的字段
Py_XINCREF(descr_proto->fields);
descr->fields = descr_proto->fields;
// 增加原型描述符的名称的引用计数并将其赋给描述符的名称
Py_XINCREF(descr_proto->names);
descr->names = descr_proto->names;
// 增加原型描述符的元数据的引用计数并将其赋给描述符的元数据
Py_XINCREF(descr_proto->metadata);
descr->metadata = descr_proto->metadata;
// 如果原型描述符的 C 元数据不为空,则克隆它并赋给描述符的 C 元数据;否则置空描述符的 C 元数据
if (descr_proto->c_metadata != NULL) {
descr->c_metadata = NPY_AUXDATA_CLONE(descr_proto->c_metadata);
}
else {
descr->c_metadata = NULL;
}
// 将描述符的哈希值置为无效值(假定字段未设置)
descr->hash = -1;
// 将描述符添加到用户描述符数组中,并增加用户描述符数量计数
userdescrs[NPY_NUMUSERTYPES++] = descr;
// 将描述符的类型编号设置为指定的类型编号
descr->type_num = typenum;
// 更新原型描述符的类型编号以便于检测重复注册
descr_proto->type_num = typenum;
// 尝试包装旧版本的描述符以处理遗留描述符,如果失败则进行回滚
if (dtypemeta_wrap_legacy_descriptor(
descr, descr_proto->f, &PyArrayDescr_Type, name, NULL) < 0) {
descr->type_num = -1;
NPY_NUMUSERTYPES--;
// 覆盖描述符的类型,防止错误导致解除引用崩溃
Py_SET_TYPE(descr, &PyArrayDescr_Type);
Py_DECREF(descr);
PyMem_Free(name); // 只有在失败时释放名称
return -1;
}
// 如果使用了 void_clearimpl,则设置相应的清除和零填充循环
if (use_void_clearimpl) {
// 设置清除循环函数指针
NPY_DT_SLOTS(NPY_DTYPE(descr))->get_clear_loop = (
(PyArrayMethod_GetTraverseLoop *)&npy_get_clear_void_and_legacy_user_dtype_loop);
// 设置零填充循环函数指针
NPY_DT_SLOTS(NPY_DTYPE(descr))->get_fill_zero_loop = (
(PyArrayMethod_GetTraverseLoop *)&npy_get_zerofill_void_and_legacy_user_dtype_loop);
}
// 返回设置的类型编号
return typenum;
/*
* 检查是否已经使用新的转换实现机制缓存了转换。
* 如果是,则不清空缓存(但是会默默地继续)。用户在使用后不应修改转换,
* 但这可能在设置过程中意外发生(也可能从未发生)。参见 https://github.com/numpy/numpy/issues/20009
*/
static int _warn_if_cast_exists_already(
PyArray_Descr *descr, int totype, char *funcname)
{
// 从类型编号获取对应的数据类型元数据对象
PyArray_DTypeMeta *to_DType = PyArray_DTypeFromTypeNum(totype);
if (to_DType == NULL) {
return -1;
}
// 从字典中获取对应转换实现的对象
PyObject *cast_impl = PyDict_GetItemWithError(
NPY_DT_SLOTS(NPY_DTYPE(descr))->castingimpls, (PyObject *)to_DType);
Py_DECREF(to_DType);
if (cast_impl == NULL) {
if (PyErr_Occurred()) {
return -1;
}
}
else {
// 如果获取到转换实现对象
char *extra_msg;
if (cast_impl == Py_None) {
extra_msg = "the cast will continue to be considered impossible.";
}
else {
extra_msg = "the previous definition will continue to be used.";
}
Py_DECREF(cast_impl);
// 根据类型编号获取对应的描述符对象
PyArray_Descr *to_descr = PyArray_DescrFromType(totype);
// 发出运行时警告,说明转换注册/修改发生在使用后
int ret = PyErr_WarnFormat(PyExc_RuntimeWarning, 1,
"A cast from %R to %R was registered/modified using `%s` "
"after the cast had been used. "
"This registration will have (mostly) no effect: %s\n"
"The most likely fix is to ensure that casts are the first "
"thing initialized after dtype registration. "
"Please contact the NumPy developers with any questions!",
descr, to_descr, funcname, extra_msg);
Py_DECREF(to_descr);
if (ret < 0) {
return -1;
}
}
return 0;
}
/*
* NUMPY_API
* 注册转换函数
* 替换当前存储的任何函数。
*/
NPY_NO_EXPORT int
PyArray_RegisterCastFunc(PyArray_Descr *descr, int totype,
PyArray_VectorUnaryFunc *castfunc)
{
PyObject *cobj, *key;
int ret;
// 检查类型编号是否有效
if (totype >= NPY_NTYPES_LEGACY && !PyTypeNum_ISUSERDEF(totype)) {
PyErr_SetString(PyExc_TypeError, "invalid type number.");
return -1;
}
// 检查是否已经存在相同转换的警告
if (_warn_if_cast_exists_already(
descr, totype, "PyArray_RegisterCastFunc") < 0) {
return -1;
}
// 如果类型编号小于 NPY_NTYPES_ABI_COMPATIBLE,直接设置转换函数
if (totype < NPY_NTYPES_ABI_COMPATIBLE) {
PyDataType_GetArrFuncs(descr)->cast[totype] = castfunc;
return 0;
}
// 如果转换函数字典尚未初始化,初始化它
if (PyDataType_GetArrFuncs(descr)->castdict == NULL) {
PyDataType_GetArrFuncs(descr)->castdict = PyDict_New();
if (PyDataType_GetArrFuncs(descr)->castdict == NULL) {
return -1;
}
}
// 创建用于作为字典键的 Python 整数对象
key = PyLong_FromLong(totype);
if (PyErr_Occurred()) {
return -1;
}
// 创建转换函数对象的 Capsule 对象
cobj = PyCapsule_New((void *)castfunc, NULL, NULL);
if (cobj == NULL) {
Py_DECREF(key);
return -1;
}
ret = PyDict_SetItem(PyDataType_GetArrFuncs(descr)->castdict, key, cobj);
Py_DECREF(key);
Py_DECREF(cobj);
return ret;
/*NUMPY_API
* Register a type number indicating that a descriptor can be cast
* to it safely
*/
NPY_NO_EXPORT int
PyArray_RegisterCanCast(PyArray_Descr *descr, int totype,
NPY_SCALARKIND scalar)
{
/*
* 如果允许这样做,内置类型的类型转换查找表需要修改,
* 因为对于它们,不会检查 cancastto。
*/
if (!PyTypeNum_ISUSERDEF(descr->type_num) &&
!PyTypeNum_ISUSERDEF(totype)) {
PyErr_SetString(PyExc_ValueError,
"At least one of the types provided to "
"RegisterCanCast must be user-defined.");
return -1;
}
if (_warn_if_cast_exists_already(
descr, totype, "PyArray_RegisterCanCast") < 0) {
return -1;
}
if (scalar == NPY_NOSCALAR) {
/*
* 使用 cancastto 进行注册
* 这些列表一旦创建就不会被释放
* —— 它们成为数据类型的一部分
*/
if (PyDataType_GetArrFuncs(descr)->cancastto == NULL) {
PyDataType_GetArrFuncs(descr)->cancastto = (int *)malloc(1*sizeof(int));
if (PyDataType_GetArrFuncs(descr)->cancastto == NULL) {
PyErr_NoMemory();
return -1;
}
PyDataType_GetArrFuncs(descr)->cancastto[0] = NPY_NOTYPE;
}
return _append_new(&PyDataType_GetArrFuncs(descr)->cancastto, totype);
}
else {
/* 使用 cancastscalarkindto 进行注册 */
if (PyDataType_GetArrFuncs(descr)->cancastscalarkindto == NULL) {
int i;
PyDataType_GetArrFuncs(descr)->cancastscalarkindto =
(int **)malloc(NPY_NSCALARKINDS* sizeof(int*));
if (PyDataType_GetArrFuncs(descr)->cancastscalarkindto == NULL) {
PyErr_NoMemory();
return -1;
}
for (i = 0; i < NPY_NSCALARKINDS; i++) {
PyDataType_GetArrFuncs(descr)->cancastscalarkindto[i] = NULL;
}
}
if (PyDataType_GetArrFuncs(descr)->cancastscalarkindto[scalar] == NULL) {
PyDataType_GetArrFuncs(descr)->cancastscalarkindto[scalar] =
(int *)malloc(1*sizeof(int));
if (PyDataType_GetArrFuncs(descr)->cancastscalarkindto[scalar] == NULL) {
PyErr_NoMemory();
return -1;
}
PyDataType_GetArrFuncs(descr)->cancastscalarkindto[scalar][0] =
NPY_NOTYPE;
}
return _append_new(&PyDataType_GetArrFuncs(descr)->cancastscalarkindto[scalar], totype);
}
}
// 定义 legacy_userdtype_common_dtype_function 函数,参数为两个 PyArray_DTypeMeta 类型的指针
legacy_userdtype_common_dtype_function(
PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other)
{
// 初始化 skind1 和 skind2 为 NPY_NOSCALAR
int skind1 = NPY_NOSCALAR, skind2 = NPY_NOSCALAR, skind;
// 如果 other 不是 legacy 类型,则可以始终退化为新样式类型
if (!NPY_DT_is_legacy(other)) {
// 增加 Py_NotImplemented 的引用计数,并返回 Py_NotImplemented 指针
Py_INCREF(Py_NotImplemented);
return (PyArray_DTypeMeta *)Py_NotImplemented;
}
// 推迟处理,以便只有一种类型处理转换
if (cls->type_num < other->type_num) {
// 增加 Py_NotImplemented 的引用计数,并返回 Py_NotImplemented 指针
Py_INCREF(Py_NotImplemented);
return (PyArray_DTypeMeta *)Py_NotImplemented;
}
// 检查是否可以安全地从一种类型转换为另一种类型
if (PyArray_CanCastSafely(cls->type_num, other->type_num)) {
// 增加 other 的引用计数,并返回 other 指针
Py_INCREF(other);
return other;
}
if (PyArray_CanCastSafely(other->type_num, cls->type_num)) {
// 增加 cls 的引用计数,并返回 cls 指针
Py_INCREF(cls);
return cls;
}
/*
* 以下代码曾是 PyArray_PromoteTypes() 的一部分。
* 可以预期这段代码不会被使用。
* 实际上,它允许将两种不同的用户定义类型提升为相同 "kind" 的 NumPy 类型。
* 在实践中,由于 PyArray_EquivTypes(descr1, descr2) 的简化,使用与 NumPy 相同的 `kind` 从未可能。
* 如果 kind 和元素大小匹配,则 PyArray_EquivTypes(descr1, descr2) 返回 True(例如,bfloat16 和 float16 将等效)。
* 这个选项也非常难以理解,并且在示例中没有使用。
*/
// 将 'kind' 字符转换为标量 kind
switch (cls->singleton->kind) {
case 'b':
skind1 = NPY_BOOL_SCALAR;
break;
case 'u':
skind1 = NPY_INTPOS_SCALAR;
break;
case 'i':
skind1 = NPY_INTNEG_SCALAR;
break;
case 'f':
skind1 = NPY_FLOAT_SCALAR;
break;
case 'c':
skind1 = NPY_COMPLEX_SCALAR;
break;
}
switch (other->singleton->kind) {
case 'b':
skind2 = NPY_BOOL_SCALAR;
break;
case 'u':
skind2 = NPY_INTPOS_SCALAR;
break;
case 'i':
skind2 = NPY_INTNEG_SCALAR;
break;
case 'f':
skind2 = NPY_FLOAT_SCALAR;
break;
case 'c':
skind2 = NPY_COMPLEX_SCALAR;
break;
}
// 如果两者都是标量,可能会存在一种提升的可能性
if (skind1 != NPY_NOSCALAR && skind2 != NPY_NOSCALAR) {
skind = (skind1 > skind2) ? skind1 : skind2;
int ret_type_num = _npy_smallest_type_of_kind_table[skind];
for (;;) {
if (ret_type_num < 0) {
++skind;
if (skind < NPY_NSCALARKINDS) {
ret_type_num = _npy_smallest_type_of_kind_table[skind];
}
else {
break;
}
}
if (PyArray_CanCastSafely(cls->type_num, ret_type_num) &&
PyArray_CanCastSafely(other->type_num, ret_type_num)) {
return PyArray_DTypeFromTypeNum(ret_type_num);
}
ret_type_num = _npy_next_larger_type_table[ret_type_num];
}
}
Py_INCREF(Py_NotImplemented);
return (PyArray_DTypeMeta *)Py_NotImplemented;
/**
* This function wraps a legacy cast into an array-method. This is mostly
* used for legacy user-dtypes, but for example numeric to/from datetime
* casts were only defined that way as well.
*
* @param from Source data type metadata for casting.
* @param to Target data type metadata for casting.
* @param casting Casting behavior specifier: `NPY_NO_CASTING` checks the legacy
* registered cast, otherwise uses the provided cast.
*/
NPY_NO_EXPORT int
PyArray_AddLegacyWrapping_CastingImpl(
PyArray_DTypeMeta *from, PyArray_DTypeMeta *to, NPY_CASTING casting)
{
// Determine the casting behavior if not explicitly provided
if (casting < 0) {
// Check if source and target types are the same
if (from == to) {
casting = NPY_NO_CASTING;
}
// Check for safe casting capability
else if (PyArray_LegacyCanCastTypeTo(
from->singleton, to->singleton, NPY_SAFE_CASTING)) {
casting = NPY_SAFE_CASTING;
}
// Check for same-kind casting capability
else if (PyArray_LegacyCanCastTypeTo(
from->singleton, to->singleton, NPY_SAME_KIND_CASTING)) {
casting = NPY_SAME_KIND_CASTING;
}
// Default to unsafe casting if no other options are suitable
else {
casting = NPY_UNSAFE_CASTING;
}
}
// Prepare an array method specification for legacy casting
PyArray_DTypeMeta *dtypes[2] = {from, to};
PyArrayMethod_Spec spec = {
/* Name is not actually used, but allows identifying these. */
.name = "legacy_cast",
.nin = 1,
.nout = 1,
.casting = casting,
.dtypes = dtypes,
};
// Define method flags and slots based on whether types are identical or not
if (from == to) {
spec.flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_SUPPORTS_UNALIGNED;
// Define slots for identical type casting
PyType_Slot slots[] = {
{NPY_METH_get_loop, &legacy_cast_get_strided_loop},
{NPY_METH_resolve_descriptors, &legacy_same_dtype_resolve_descriptors},
{0, NULL}};
spec.slots = slots;
// Add the casting implementation using the specified method specification
return PyArray_AddCastingImplementation_FromSpec(&spec, 1);
}
else {
spec.flags = NPY_METH_REQUIRES_PYAPI;
// Define slots for different type casting
PyType_Slot slots[] = {
{NPY_METH_get_loop, &legacy_cast_get_strided_loop},
{NPY_METH_resolve_descriptors, &simple_cast_resolve_descriptors},
{0, NULL}};
spec.slots = slots;
// Add the casting implementation using the specified method specification
return PyArray_AddCastingImplementation_FromSpec(&spec, 1);
}
}
.\numpy\numpy\_core\src\multiarray\usertypes.h
extern NPY_NO_EXPORT _PyArray_LegacyDescr **userdescrs; // 声明一个名为 userdescrs 的外部全局变量,类型为 _PyArray_LegacyDescr**,在其他文件中可见
// 初始化数组函数指针数组结构体
NPY_NO_EXPORT void
PyArray_InitArrFuncs(PyArray_ArrFuncs *f);
// 注册能够进行类型转换的方法
NPY_NO_EXPORT int
PyArray_RegisterCanCast(PyArray_Descr *descr, int totype,
NPY_SCALARKIND scalar);
// 注册数据类型描述符
NPY_NO_EXPORT int
PyArray_RegisterDataType(PyArray_DescrProto *descr);
// 注册类型转换函数
NPY_NO_EXPORT int
PyArray_RegisterCastFunc(PyArray_Descr *descr, int totype,
PyArray_VectorUnaryFunc *castfunc);
// 用于确定两个用户定义数据类型的公共数据类型
NPY_NO_EXPORT PyArray_DTypeMeta *
legacy_userdtype_common_dtype_function(
PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other);
// 添加旧版包装的转换实现
NPY_NO_EXPORT int
PyArray_AddLegacyWrapping_CastingImpl(
PyArray_DTypeMeta *from, PyArray_DTypeMeta *to, NPY_CASTING casting);
.\numpy\numpy\_core\src\multiarray\vdot.c
/*
* 定义宏,禁用已弃用的 NumPy API,并指定使用当前版本的 API
* 定义宏,指示要包含 multiarray 模块
*/
/*
* 定义宏,确保在包含 Python.h 之前清理 PY_SSIZE_T 的定义
* 包含 Python.h 头文件,提供 Python C API 的支持
*/
/*
* 包含常用函数的头文件
* 包含向量点积相关函数的头文件
* 包含与 numpy 的 CBLAS 接口相关的头文件
*/
/*
* 所有数据假定是对齐的。
* 计算复数浮点数向量点积的函数
*/
NPY_NO_EXPORT void
CFLOAT_vdot(char *ip1, npy_intp is1, char *ip2, npy_intp is2,
char *op, npy_intp n, void *NPY_UNUSED(ignore))
{
// 计算每个输入向量的步长,确保对齐
CBLAS_INT is1b = blas_stride(is1, sizeof(npy_cfloat));
CBLAS_INT is2b = blas_stride(is2, sizeof(npy_cfloat));
// 如果步长均合法,则使用 CBLAS 函数计算点积
if (is1b && is2b) {
// 使用双精度浮点数数组保持计算稳定性
double sum[2] = {0., 0.};
// 循环计算向量点积,使用块处理以提高效率
while (n > 0) {
// 确定当前处理的块大小
CBLAS_INT chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK;
float tmp[2];
// 调用 CBLAS 函数计算向量点积的实部和虚部
CBLAS_FUNC(cblas_cdotc_sub)((CBLAS_INT)n, ip1, is1b, ip2, is2b, tmp);
sum[0] += (double)tmp[0];
sum[1] += (double)tmp[1];
// 根据步长更新指针位置,准备处理下一个块
ip1 += chunk * is1;
ip2 += chunk * is2;
n -= chunk;
}
// 将计算结果存入输出数组中
((float *)op)[0] = (float)sum[0];
((float *)op)[1] = (float)sum[1];
}
// 如果步长不合法,则使用简单的循环计算向量点积
else
{
// 初始化实部和虚部的和
float sumr = (float)0.0;
float sumi = (float)0.0;
npy_intp i;
// 循环计算向量点积的实部和虚部
for (i = 0; i < n; i++, ip1 += is1, ip2 += is2) {
// 提取每个向量元素的实部和虚部
const float ip1r = ((float *)ip1)[0];
const float ip1i = ((float *)ip1)[1];
const float ip2r = ((float *)ip2)[0];
const float ip2i = ((float *)ip2)[1];
// 计算向量点积的实部和虚部
sumr += ip1r * ip2r + ip1i * ip2i;
sumi += ip1r * ip2i - ip1i * ip2r;
}
// 将计算结果存入输出数组中
((float *)op)[0] = sumr;
((float *)op)[1] = sumi;
}
}
/*
* 所有数据假定是对齐的。
* 计算双精度复数向量点积的函数
*/
NPY_NO_EXPORT void
CDOUBLE_vdot(char *ip1, npy_intp is1, char *ip2, npy_intp is2,
char *op, npy_intp n, void *NPY_UNUSED(ignore))
{
// 计算每个输入向量的步长,确保对齐
CBLAS_INT is1b = blas_stride(is1, sizeof(npy_cdouble));
CBLAS_INT is2b = blas_stride(is2, sizeof(npy_cdouble));
// 如果步长均合法,则使用 CBLAS 函数计算点积
if (is1b && is2b) {
// 使用双精度浮点数数组保持计算稳定性
double sum[2] = {0., 0.};
// 循环计算向量点积,使用块处理以提高效率
while (n > 0) {
// 确定当前处理的块大小
CBLAS_INT chunk = n < NPY_CBLAS_CHUNK ? n : NPY_CBLAS_CHUNK;
double tmp[2];
// 调用 CBLAS 函数计算向量点积的实部和虚部
CBLAS_FUNC(cblas_zdotc_sub)((CBLAS_INT)n, ip1, is1b, ip2, is2b, tmp);
sum[0] += (double)tmp[0];
sum[1] += (double)tmp[1];
// 根据步长更新指针位置,准备处理下一个块
ip1 += chunk * is1;
ip2 += chunk * is2;
n -= chunk;
}
// 将计算结果存入输出数组中
((double *)op)[0] = (double)sum[0];
((double *)op)[1] = (double)sum[1];
}
// 如果步长不合法,则使用简单的循环计算向量点积
else
{
// 初始化实部和虚部的总和为0
double sumr = (double)0.0;
double sumi = (double)0.0;
// 循环迭代器
npy_intp i;
// 遍历从0到n的范围
for (i = 0; i < n; i++, ip1 += is1, ip2 += is2) {
// 提取ip1和ip2的实部和虚部
const double ip1r = ((double *)ip1)[0];
const double ip1i = ((double *)ip1)[1];
const double ip2r = ((double *)ip2)[0];
const double ip2i = ((double *)ip2)[1];
// 计算实部和虚部的加权和
sumr += ip1r * ip2r + ip1i * ip2i;
// 计算实部和虚部的差值
sumi += ip1r * ip2i - ip1i * ip2r;
}
// 将累积的实部和虚部写入op的内存位置
((double *)op)[0] = sumr;
((double *)op)[1] = sumi;
}
/*
* 该函数计算复数向量的内积,并将结果存储在输出数组中。
* 输入参数 ip1 和 ip2 是输入数组的指针,is1 和 is2 是它们的步长。
* 输出参数 op 是输出数组的指针,n 是数组的长度。
* ignore 参数未使用。
*/
NPY_NO_EXPORT void
CLONGDOUBLE_vdot(char *ip1, npy_intp is1, char *ip2, npy_intp is2,
char *op, npy_intp n, void *NPY_UNUSED(ignore))
{
// 初始化实部和虚部的临时变量为0
npy_longdouble tmpr = 0.0L;
npy_longdouble tmpi = 0.0L;
npy_intp i;
// 循环计算复数向量的内积
for (i = 0; i < n; i++, ip1 += is1, ip2 += is2) {
// 从输入数组中读取复数的实部和虚部
const npy_longdouble ip1r = ((npy_longdouble *)ip1)[0];
const npy_longdouble ip1i = ((npy_longdouble *)ip1)[1];
const npy_longdouble ip2r = ((npy_longdouble *)ip2)[0];
const npy_longdouble ip2i = ((npy_longdouble *)ip2)[1];
// 计算复数向量的实部和虚部的内积
tmpr += ip1r * ip2r + ip1i * ip2i;
tmpi += ip1r * ip2i - ip1i * ip2r;
}
// 将计算结果存储到输出数组中
((npy_longdouble *)op)[0] = tmpr;
((npy_longdouble *)op)[1] = tmpi;
}
/*
* 该函数计算对象数组的点积(dot product),并将结果存储在输出对象中。
* 输入参数 ip1 和 ip2 是输入对象数组的指针,is1 和 is2 是它们的步长。
* 输出参数 op 是输出对象的指针,n 是对象数组的长度。
* ignore 参数未使用。
*/
NPY_NO_EXPORT void
OBJECT_vdot(char *ip1, npy_intp is1, char *ip2, npy_intp is2, char *op, npy_intp n,
void *NPY_UNUSED(ignore))
{
npy_intp i;
PyObject *tmp0, *tmp1, *tmp2, *tmp = NULL;
PyObject **tmp3;
// 循环计算对象数组的点积
for (i = 0; i < n; i++, ip1 += is1, ip2 += is2) {
// 检查输入对象是否为空
if ((*((PyObject **)ip1) == NULL) || (*((PyObject **)ip2) == NULL)) {
tmp1 = Py_False;
Py_INCREF(Py_False); // 增加 Py_False 的引用计数
}
else {
// 调用第一个对象的 conjugate 方法
tmp0 = PyObject_CallMethod(*((PyObject **)ip1), "conjugate", NULL);
if (tmp0 == NULL) {
Py_XDECREF(tmp); // 减少临时对象的引用计数
return;
}
// 计算两个对象的乘积
tmp1 = PyNumber_Multiply(tmp0, *((PyObject **)ip2));
Py_DECREF(tmp0); // 减少临时对象的引用计数
if (tmp1 == NULL) {
Py_XDECREF(tmp); // 减少临时对象的引用计数
return;
}
}
// 更新临时对象的引用
if (i == 0) {
tmp = tmp1;
}
else {
tmp2 = PyNumber_Add(tmp, tmp1);
Py_XDECREF(tmp); // 减少临时对象的引用计数
Py_XDECREF(tmp1); // 减少临时对象的引用计数
if (tmp2 == NULL) {
return;
}
tmp = tmp2;
}
}
// 将最终的临时对象赋值给输出对象
tmp3 = (PyObject**) op;
tmp2 = *tmp3;
*((PyObject **)op) = tmp;
Py_XDECREF(tmp2); // 减少临时对象的引用计数
}
.\numpy\numpy\_core\src\multiarray\vdot.h
// 包含 "common.h" 头文件,这是当前头文件的依赖文件
// 声明 CFLOAT_vdot 函数,计算复数浮点数向量的点积
NPY_NO_EXPORT void
CFLOAT_vdot(char *, npy_intp, char *, npy_intp, char *, npy_intp, void *);
// 声明 CDOUBLE_vdot 函数,计算双精度浮点数向量的点积
NPY_NO_EXPORT void
CDOUBLE_vdot(char *, npy_intp, char *, npy_intp, char *, npy_intp, void *);
// 声明 CLONGDOUBLE_vdot 函数,计算长双精度浮点数向量的点积
NPY_NO_EXPORT void
CLONGDOUBLE_vdot(char *, npy_intp, char *, npy_intp, char *, npy_intp, void *);
// 声明 OBJECT_vdot 函数,计算对象类型向量的点积
NPY_NO_EXPORT void
OBJECT_vdot(char *, npy_intp, char *, npy_intp, char *, npy_intp, void *);
// 结束条件:关闭 NUMPY_CORE_SRC_MULTIARRAY_VDOT_H_ 宏定义
.\numpy\numpy\_core\src\multiarray\_datetime.h
// 外部声明 datetime 字符串数组,用于存储日期时间单元字符串
extern NPY_NO_EXPORT char const *_datetime_strings[NPY_DATETIME_NUMUNITS];
// 外部声明 _days_per_month_table,用于存储每月天数的表格
extern NPY_NO_EXPORT int _days_per_month_table[2][12];
// 导入 Python 中的 datetime 模块
NPY_NO_EXPORT void
numpy_pydatetime_import(void);
/*
* 如果给定的年份是闰年则返回 1,否则返回 0。
*/
NPY_NO_EXPORT int
is_leapyear(npy_int64 year);
/*
* 计算从 1970 年纪元开始到指定日期时间结构的天数偏移量。
*/
NPY_NO_EXPORT npy_int64
get_datetimestruct_days(const npy_datetimestruct *dts);
/*
* 使用提供的元数据创建 datetime 或 timedelta 类型的 dtype。
*/
NPY_NO_EXPORT PyArray_Descr *
create_datetime_dtype(int type_num, PyArray_DatetimeMetaData *meta);
/*
* 使用给定的单元创建 datetime 或 timedelta 类型的 dtype。
*/
NPY_NO_EXPORT PyArray_Descr *
create_datetime_dtype_with_unit(int type_num, NPY_DATETIMEUNIT unit);
/*
* 返回 datetime dtype 中包含的 DateTimeMetaData 指针。
*/
NPY_NO_EXPORT PyArray_DatetimeMetaData *
get_datetime_metadata_from_dtype(PyArray_Descr *dtype);
/*
* 在数组中查找与 datetime64 类型匹配的字符串类型。
*/
NPY_NO_EXPORT int
find_string_array_datetime64_type(PyArrayObject *arr,
PyArray_DatetimeMetaData *meta);
/*
* type1 和 type2 必须是 NPY_DATETIME 或 NPY_TIMEDELTA。
* 应用类型提升规则,返回提升后的类型。
*/
NPY_NO_EXPORT PyArray_Descr *
datetime_type_promotion(PyArray_Descr *type1, PyArray_Descr *type2);
/*
* 从 'datetime64[D]' 值中提取当前年份内的月份编号。1 表示一月,以此类推。
*/
NPY_NO_EXPORT int
days_to_month_number(npy_datetime days);
/*
* 解析元数据字符串到元数据 C 结构中。
* 成功返回 0,失败返回 -1。
*/
NPY_NO_EXPORT int
parse_datetime_metadata_from_metastr(char const *metastr, Py_ssize_t len,
PyArray_DatetimeMetaData *out_meta);
/*
* 将日期时间类型字符串解析为 dtype 描述符对象。
* 'type' 字符串应为以 NULL 结尾的字符串,len 应为其长度。
*/
NPY_NO_EXPORT PyArray_Descr *
parse_dtype_from_datetime_typestr(char const *typestr, Py_ssize_t len);
/*
* 将由 'str' 和 'len' 组成的子字符串转换为日期时间单元的枚举值。
* 'metastr' 用于错误消息,可能为 NULL。
* 成功返回 0,失败返回 -1。
*/
NPY_NO_EXPORT NPY_DATETIMEUNIT
parse_datetime_unit_from_string(char const *str, Py_ssize_t len, char const *metastr);
/*
* 将除数转换为较小单位的倍数,用于日期时间元数据。
* 'metastr' 用于错误消息,如果除数不适用,则可以为 NULL。
* 成功返回 0,失败返回 -1。
*/
NPY_NO_EXPORT int
convert_datetime_divisor_to_multiple(PyArray_DatetimeMetaData *meta,
int den, char const *metastr);
/*
* Determines whether the 'divisor' metadata divides evenly into
* the 'dividend' metadata.
*/
NPY_NO_EXPORT npy_bool
datetime_metadata_divides(
PyArray_DatetimeMetaData *dividend,
PyArray_DatetimeMetaData *divisor,
int strict_with_nonlinear_units);
/*
* This provides the casting rules for the DATETIME data type units.
*
* Notably, there is a barrier between 'date units' and 'time units'
* for all but 'unsafe' casting.
*/
NPY_NO_EXPORT npy_bool
can_cast_datetime64_units(NPY_DATETIMEUNIT src_unit,
NPY_DATETIMEUNIT dst_unit,
NPY_CASTING casting);
/*
* This provides the casting rules for the DATETIME data type metadata.
*/
NPY_NO_EXPORT npy_bool
can_cast_datetime64_metadata(PyArray_DatetimeMetaData *src_meta,
PyArray_DatetimeMetaData *dst_meta,
NPY_CASTING casting);
/*
* This provides the casting rules for the TIMEDELTA data type units.
*
* Notably, there is a barrier between the nonlinear years and
* months units, and all the other units.
*/
NPY_NO_EXPORT npy_bool
can_cast_timedelta64_units(NPY_DATETIMEUNIT src_unit,
NPY_DATETIMEUNIT dst_unit,
NPY_CASTING casting);
/*
* This provides the casting rules for the TIMEDELTA data type metadata.
*/
NPY_NO_EXPORT npy_bool
can_cast_timedelta64_metadata(PyArray_DatetimeMetaData *src_meta,
PyArray_DatetimeMetaData *dst_meta,
NPY_CASTING casting);
/*
* Computes the conversion factor to convert data with 'src_meta' metadata
* into data with 'dst_meta' metadata.
*
* If overflow occurs, both out_num and out_denom are set to 0, but
* no error is set.
*/
NPY_NO_EXPORT void
get_datetime_conversion_factor(PyArray_DatetimeMetaData *src_meta,
PyArray_DatetimeMetaData *dst_meta,
npy_int64 *out_num, npy_int64 *out_denom);
/*
* Given a pointer to datetime metadata,
* returns a tuple for pickling and other purposes.
*/
NPY_NO_EXPORT PyObject *
convert_datetime_metadata_to_tuple(PyArray_DatetimeMetaData *meta);
/*
* Converts a metadata tuple into a datetime metadata C struct.
*
* Returns 0 on success, -1 on failure.
*/
NPY_NO_EXPORT int
convert_datetime_metadata_tuple_to_datetime_metadata(PyObject *tuple,
PyArray_DatetimeMetaData *out_meta,
npy_bool from_pickle);
/*
* Gets a tzoffset in minutes by calling the fromutc() function on
* the Python datetime.tzinfo object.
*/
NPY_NO_EXPORT int
get_tzoffset_from_pytzinfo(PyObject *timezone, npy_datetimestruct *dts);
/*
* Converts an input object into datetime metadata. The input
* may be either a string or a tuple.
*
* Returns 0 on success, -1 on failure.
*/
NPY_NO_EXPORT int
/*
* 将 PyObject 转换为 datetime 元数据。
* 返回值是一个 Unicode 对象的新引用。
* 在错误时返回 NULL。
*
* 如果 'skip_brackets' 为 true,则跳过 '[]'。
*/
NPY_NO_EXPORT PyObject *
metastr_to_unicode(PyArray_DatetimeMetaData *meta, int skip_brackets);
/*
* 将 PyObject * 转换为 datetime,支持所有支持的形式。
*
* 如果单位的元数据事先不知道,将 meta->base 设置为 -1,
* 此函数将使用默认值或输入对象中的值填充 meta。
*
* 'casting' 参数用于控制接受的输入类型以及处理方式。
* 例如,使用 'unsafe' casting,无法识别的输入将被转换为 'NaT' 而不是抛出错误,
* 而使用 'safe' casting,如果输入中有任何精度将被丢弃,则会抛出错误。
*
* 错误时返回 -1,成功时返回 0。
*/
NPY_NO_EXPORT int
convert_pyobject_to_datetime(PyArray_DatetimeMetaData *meta, PyObject *obj,
NPY_CASTING casting, npy_datetime *out);
/*
* 将 PyObject * 转换为 timedelta,支持所有支持的形式。
*
* 如果单位的元数据事先不知道,将 meta->base 设置为 -1,
* 此函数将使用默认值或输入对象中的值填充 meta。
*
* 'casting' 参数用于控制接受的输入类型以及处理方式。
* 例如,使用 'unsafe' casting,无法识别的输入将被转换为 'NaT' 而不是抛出错误,
* 而使用 'safe' casting,如果输入中有任何精度将被丢弃,则会抛出错误。
*
* 错误时返回 -1,成功时返回 0。
*/
NPY_NO_EXPORT int
convert_pyobject_to_timedelta(PyArray_DatetimeMetaData *meta, PyObject *obj,
NPY_CASTING casting, npy_timedelta *out);
/*
* 将 datetime 转换为 PyObject *。
*
* 对于天或更粗的单位,返回一个 datetime.date 对象。
* 对于微秒或更粗的单位,返回一个 datetime.datetime 对象。
* 对于精度高于微秒的单位,返回一个整数。
*/
NPY_NO_EXPORT PyObject *
convert_datetime_to_pyobject(npy_datetime dt, PyArray_DatetimeMetaData *meta);
/*
* 将 timedelta 转换为 PyObject *。
*
* 如果是 Not-a-time,则返回字符串 "NaT"。
* 对于微秒或更粗的单位,返回一个 datetime.timedelta 对象。
* 对于精度高于微秒的单位,返回一个整数。
*/
NPY_NO_EXPORT PyObject *
convert_timedelta_to_pyobject(npy_timedelta td, PyArray_DatetimeMetaData *meta);
/*
* 根据秒偏移调整 datetimestruct。假设当前值是有效的。
*/
NPY_NO_EXPORT void
add_seconds_to_datetimestruct(npy_datetimestruct *dts, int seconds);
/*
* 根据分钟偏移调整 datetimestruct。假设当前值是有效的。
*/
NPY_NO_EXPORT void
add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes);
/*
* 在给定的 npy_datetimestruct 结构体上增加指定的分钟数
*/
add_minutes_to_datetimestruct(npy_datetimestruct *dts, int minutes);
/*
* 检查两个 datetime 类型的描述符是否具有等效的元数据
* 如果元数据匹配,则返回 true
*/
NPY_NO_EXPORT npy_bool
has_equivalent_datetime_metadata(PyArray_Descr *type1, PyArray_Descr *type2);
/*
* 将单个 datetime 从 src_meta 元数据转换为 dst_meta 元数据
* 成功时返回 0,失败时返回 -1
*/
NPY_NO_EXPORT int
cast_datetime_to_datetime(PyArray_DatetimeMetaData *src_meta,
PyArray_DatetimeMetaData *dst_meta,
npy_datetime src_dt,
npy_datetime *dst_dt);
/*
* 将单个 timedelta 从 src_meta 元数据转换为 dst_meta 元数据
* 成功时返回 0,失败时返回 -1
*/
NPY_NO_EXPORT int
cast_timedelta_to_timedelta(PyArray_DatetimeMetaData *src_meta,
PyArray_DatetimeMetaData *dst_meta,
npy_timedelta src_dt,
npy_timedelta *dst_dt);
/*
* 检查对象是否最适合视为 Datetime 或 Timedelta
* 如果是,则返回 true;否则返回 false
*/
NPY_NO_EXPORT npy_bool
is_any_numpy_datetime_or_timedelta(PyObject *obj);
/*
* 实现特定于 datetime 的 arange 函数
*/
NPY_NO_EXPORT PyArrayObject *
datetime_arange(PyObject *start, PyObject *stop, PyObject *step,
PyArray_Descr *dtype);
/*
* 通过递归下降序列结构来检查给定 Python 对象中的所有对象
* 返回基于数据的 datetime 或 timedelta 类型的描述符
*/
NPY_NO_EXPORT PyArray_Descr *
find_object_datetime_type(PyObject *obj, int type_num);
/*
* 初始化 datetime 类型的强制转换规则
* 成功时返回 0,失败时返回 -1
*/
NPY_NO_EXPORT int
PyArray_InitializeDatetimeCasts(void);
.\numpy\numpy\_core\src\npymath\arm64_exports.c
/*
* 对于 macOS arm64 平台,导出这些函数供 SciPy 使用。
* SciPy 在 macOS arm64 上构建时,会下载 macOS x86_64 平台的 NumPy,
* 并且链接器在无法使用 npymathlib.a 时不会报错。导入 numpy 会暴露这些外部函数。
* 参考 https://github.com/numpy/numpy/issues/22673
*
* 实际上,这个文件会作为主模块的一部分进行编译。
*/
// 计算反双曲正弦值
double npy_asinh(double x) {
return asinh(x);
}
// 返回带有给定符号的 y 的值
double npy_copysign(double y, double x) {
return copysign(y, x);
}
// 返回 log(1+x)
double npy_log1p(double x) {
return log1p(x);
}
// 返回靠近 x 但大于 y 的浮点数
double npy_nextafter(double x, double y) {
return nextafter(x, y);
}
.\numpy\numpy\_core\src\npymath\halffloat.cpp
/*
* 如果设置为1,转换过程中会尝试在需要时触发浮点系统的下溢、上溢和无效异常。
*/
/*
********************************************************************
* HALF-PRECISION ROUTINES *
********************************************************************
*/
using namespace np;
/*
* 将半精度浮点数转换为单精度浮点数
*/
float npy_half_to_float(npy_half h)
{
return static_cast<float>(Half::FromBits(h));
}
/*
* 将半精度浮点数转换为双精度浮点数
*/
double npy_half_to_double(npy_half h)
{
return static_cast<double>(Half::FromBits(h));
}
/*
* 将单精度浮点数转换为半精度浮点数
*/
npy_half npy_float_to_half(float f)
{
return Half(f).Bits();
}
/*
* 将双精度浮点数转换为半精度浮点数
*/
npy_half npy_double_to_half(double d)
{
return Half(d).Bits();
}
/*
* 判断半精度浮点数是否为零
*/
int npy_half_iszero(npy_half h)
{
return (h&0x7fff) == 0;
}
/*
* 判断半精度浮点数是否为NaN
*/
int npy_half_isnan(npy_half h)
{
return Half::FromBits(h).IsNaN();
}
/*
* 判断半精度浮点数是否为无穷大
*/
int npy_half_isinf(npy_half h)
{
return ((h&0x7fffu) == 0x7c00u);
}
/*
* 判断半精度浮点数是否为有限数
*/
int npy_half_isfinite(npy_half h)
{
return ((h&0x7c00u) != 0x7c00u);
}
/*
* 判断半精度浮点数的符号位是否为1(负数)
*/
int npy_half_signbit(npy_half h)
{
return (h&0x8000u) != 0;
}
/*
* 计算两个半精度浮点数之间的距离
*/
npy_half npy_half_spacing(npy_half h)
{
npy_half ret;
npy_uint16 h_exp = h&0x7c00u;
npy_uint16 h_sig = h&0x03ffu;
if (h_exp == 0x7c00u) {
npy_set_floatstatus_invalid();
ret = NPY_HALF_NAN;
} else if (h == 0x7bffu) {
npy_set_floatstatus_overflow();
ret = NPY_HALF_PINF;
} else if ((h&0x8000u) && h_sig == 0) { /* 负数边界情况 */
if (h_exp > 0x2c00u) { /* 如果结果是规格化的 */
ret = h_exp - 0x2c00u;
} else if(h_exp > 0x0400u) { /* 结果是次规格化的,但不是最小的 */
ret = 1 << ((h_exp >> 10) - 2);
} else {
ret = 0x0001u; /* 最小的次规格化半精度浮点数 */
}
} else if (h_exp > 0x2800u) { /* 如果结果仍然是规格化的 */
ret = h_exp - 0x2800u;
} else if (h_exp > 0x0400u) { /* 结果是次规格化的,但不是最小的 */
ret = 1 << ((h_exp >> 10) - 1);
} else {
ret = 0x0001u;
}
return ret;
}
/*
* 返回一个半精度浮点数,其值与x相同,但其符号与y相同
*/
npy_half npy_half_copysign(npy_half x, npy_half y)
{
return (x&0x7fffu) | (y&0x8000u);
}
/*
* 返回x和y之间的下一个半精度浮点数
*/
npy_half npy_half_nextafter(npy_half x, npy_half y)
{
npy_half ret;
if (npy_half_isnan(x) || npy_half_isnan(y)) {
ret = NPY_HALF_NAN;
} else if (npy_half_eq_nonan(x, y)) {
ret = x;
} else if (npy_half_iszero(x)) {
ret = (y&0x8000u) + 1; /* 最小的次规格化半精度浮点数 */
} else if (!(x&0x8000u)) { /* x > 0 */
if ((npy_int16)x > (npy_int16)y) { /* x > y */
ret = x-1;
} else {
ret = x+1;
}
} else {
// 如果 y 的符号位为 0 或者 x 的绝对值大于 y 的绝对值,则 x < y
if (!(y&0x8000u) || (x&0x7fffu) > (y&0x7fffu)) { /* x < y */
// 如果 x < y,则返回 x 减去 1
ret = x-1;
} else {
// 否则返回 x 加上 1
ret = x+1;
}
}
// 如果结果溢出且输入值不是无穷大,则设置浮点数状态为溢出
if (npy_half_isinf(ret) && npy_half_isfinite(x)) {
npy_set_floatstatus_overflow();
}
// 返回计算结果
return ret;
}
// 比较两个非 NaN 的 npy_half 类型的值是否相等
int npy_half_eq_nonan(npy_half h1, npy_half h2)
{
return Half::FromBits(h1).Equal(Half::FromBits(h2));
}
// 比较两个 npy_half 类型的值是否相等
int npy_half_eq(npy_half h1, npy_half h2)
{
return Half::FromBits(h1) == Half::FromBits(h2);
}
// 比较两个 npy_half 类型的值是否不相等
int npy_half_ne(npy_half h1, npy_half h2)
{
return Half::FromBits(h1) != Half::FromBits(h2);
}
// 比较两个非 NaN 的 npy_half 类型的值是否 h1 小于 h2
int npy_half_lt_nonan(npy_half h1, npy_half h2)
{
return Half::FromBits(h1).Less(Half::FromBits(h2));
}
// 比较两个 npy_half 类型的值是否 h1 小于 h2
int npy_half_lt(npy_half h1, npy_half h2)
{
return Half::FromBits(h1) < Half::FromBits(h2);
}
// 比较两个 npy_half 类型的值是否 h1 大于 h2
int npy_half_gt(npy_half h1, npy_half h2)
{
// 转换为 h2 < h1 的比较形式
return npy_half_lt(h2, h1);
}
// 比较两个非 NaN 的 npy_half 类型的值是否 h1 小于等于 h2
int npy_half_le_nonan(npy_half h1, npy_half h2)
{
return Half::FromBits(h1).LessEqual(Half::FromBits(h2));
}
// 比较两个 npy_half 类型的值是否 h1 小于等于 h2
int npy_half_le(npy_half h1, npy_half h2)
{
return Half::FromBits(h1) <= Half::FromBits(h2);
}
// 比较两个 npy_half 类型的值是否 h1 大于等于 h2
int npy_half_ge(npy_half h1, npy_half h2)
{
// 转换为 h2 <= h1 的比较形式
return npy_half_le(h2, h1);
}
// 计算 npy_half 类型的 h1 除以 h2 的商和余数
npy_half npy_half_divmod(npy_half h1, npy_half h2, npy_half *modulus)
{
float fh1 = npy_half_to_float(h1);
float fh2 = npy_half_to_float(h2);
float div, mod;
// 执行浮点数的除法和取余操作
div = npy_divmodf(fh1, fh2, &mod);
// 将取余结果转换为 npy_half 类型
*modulus = npy_float_to_half(mod);
// 将商的结果转换为 npy_half 类型并返回
return npy_float_to_half(div);
}
/*
********************************************************************
* BIT-LEVEL CONVERSIONS *
********************************************************************
*/
// 将 npy_uint32 类型的浮点数位表示转换为 npy_uint16 类型的半精度浮点数位表示
npy_uint16 npy_floatbits_to_halfbits(npy_uint32 f)
{
if constexpr (Half::kNativeConversion<float>) {
// 如果支持本地浮点数转换,则执行转换
return BitCast<uint16_t>(Half(BitCast<float>(f)));
}
else {
// 否则使用私有方法执行转换
return half_private::FromFloatBits(f);
}
}
// 将 npy_uint64 类型的双精度浮点数位表示转换为 npy_uint16 类型的半精度浮点数位表示
npy_uint16 npy_doublebits_to_halfbits(npy_uint64 d)
{
if constexpr (Half::kNativeConversion<double>) {
// 如果支持本地浮点数转换,则执行转换
return BitCast<uint16_t>(Half(BitCast<double>(d)));
}
else {
// 否则使用私有方法执行转换
return half_private::FromDoubleBits(d);
}
}
// 将 npy_uint16 类型的半精度浮点数位表示转换为 npy_uint32 类型的浮点数位表示
npy_uint32 npy_halfbits_to_floatbits(npy_uint16 h)
{
if constexpr (Half::kNativeConversion<float>) {
// 如果支持本地浮点数转换,则执行转换
return BitCast<uint32_t>(static_cast<float>(Half::FromBits(h)));
}
else {
// 否则使用私有方法执行转换
return half_private::ToFloatBits(h);
}
}
// 将 npy_uint16 类型的半精度浮点数位表示转换为 npy_uint64 类型的双精度浮点数位表示
npy_uint64 npy_halfbits_to_doublebits(npy_uint16 h)
{
if constexpr (Half::kNativeConversion<double>) {
// 如果支持本地浮点数转换,则执行转换
return BitCast<uint64_t>(static_cast<double>(Half::FromBits(h)));
}
else {
// 否则使用私有方法执行转换
return half_private::ToDoubleBits(h);
}
}
.\numpy\numpy\_core\src\npymath\ieee754.cpp
/*
* -*- c -*-
*
* Low-level routines related to IEEE-754 format
*/
/*
* vim:syntax=c
*
* Low-level routines related to IEEE-754 format
*/
/*
* The below code is provided for compilers which do not yet provide C11
* compatibility (gcc 4.5 and older)
*/
/*
* FIXME: There is a lot of redundancy between _next* and npy_nextafter*.
* refactor this at some point
*
* p >= 0, return x + nulp
* p < 0, return x - nulp
*/
static double
_next(double x, int p)
{
volatile double t;
npy_int32 hx, hy, ix;
npy_uint32 lx;
EXTRACT_WORDS(hx, lx, x);
ix = hx & 0x7fffffff; /* |x| */
if (((ix >= 0x7ff00000) && ((ix - 0x7ff00000) | lx) != 0)) /* x is nan */
return x;
if ((ix | lx) == 0) { /* x == 0 */
if (p >= 0) {
INSERT_WORDS(x, 0x0, 1); /* return +minsubnormal */
}
else {
INSERT_WORDS(x, 0x80000000, 1); /* return -minsubnormal */
}
t = x * x;
if (t == x)
return t;
else
return x; /* raise underflow flag */
}
if (p < 0) { /* x -= ulp */
if (lx == 0)
hx -= 1;
lx -= 1;
}
else { /* x += ulp */
lx += 1;
if (lx == 0)
hx += 1;
}
hy = hx & 0x7ff00000;
if (hy >= 0x7ff00000)
return x + x; /* overflow */
if (hy < 0x00100000) { /* underflow */
t = x * x;
if (t != x) { /* raise underflow flag */
INSERT_WORDS(x, hx, lx);
return x;
}
}
INSERT_WORDS(x, hx, lx);
return x;
}
/*
* FIXME: There is a lot of redundancy between _next* and npy_nextafter*.
* refactor this at some point
*
* p >= 0, return x + nulp
* p < 0, return x - nulp
*/
static float
_next(float x, int p)
{
volatile float t;
npy_int32 hx, hy, ix;
GET_FLOAT_WORD(hx, x);
ix = hx & 0x7fffffff; /* |x| */
if ((ix > 0x7f800000)) /* x is nan */
return x;
if (ix == 0) { /* x == 0 */
if (p >= 0) {
SET_FLOAT_WORD(x, 0x0 | 1); /* return +minsubnormal */
}
else {
SET_FLOAT_WORD(x, 0x80000000 | 1); /* return -minsubnormal */
}
t = x * x;
if (t == x)
return t;
else
return x; /* raise underflow flag */
}
if (p < 0) { /* x -= ulp */
hx -= 1;
}
else { /* x += ulp */
hx += 1;
}
hy = hx & 0x7f800000;
if (hy >= 0x7f800000)
return x + x; /* overflow */
if (hy < 0x00800000) { /* underflow */
t = x * x;
if (t != x) { /* raise underflow flag */
SET_FLOAT_WORD(x, hx);
return x;
}
}
SET_FLOAT_WORD(x, hx);
return x;
}
defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_LE)
/*
* FIXME: this is ugly and untested. The asm part only works with gcc, and we
* should consolidate the GET_LDOUBLE* / SET_LDOUBLE macros
*/
({ \
__typeof(x) __x = x; \
__asm("" : "+m"(__x)); \
__x; \
})
/* 定义宏 math_force_eval(x),强制评估 x 的值 */
/* 定义联合体 ieee854_long_double_shape_type,用于处理 IEEE 854 格式的长双精度浮点数 */
typedef union {
npy_longdouble value; /* 长双精度浮点数值 */
struct {
npy_uint64 msw; /* 高位 64 位整数部分 */
npy_uint64 lsw; /* 低位 64 位整数部分 */
} parts64;
struct {
npy_uint32 w0, w1, w2, w3; /* 32 位整数部分分解 */
} parts32;
} ieee854_long_double_shape_type;
/* 从长双精度浮点数中获取两个 64 位整数 */
do { \
ieee854_long_double_shape_type qw_u; \
qw_u.value = (d); \
(ix0) = qw_u.parts64.msw; \
(ix1) = qw_u.parts64.lsw; \
} while (0)
/* 将两个 64 位整数设置为长双精度浮点数 */
do { \
ieee854_long_double_shape_type qw_u; \
qw_u.parts64.msw = (ix0); \
qw_u.parts64.lsw = (ix1); \
(d) = qw_u.value; \
} while (0)
/* 静态函数 _next,用于实现浮点数运算 */
static long double
_next(long double x, int p)
{
npy_int64 hx, ihx, ilx; /* 高位和低位整数部分 */
npy_uint64 lx; /* 无符号长整数部分 */
npy_longdouble u; /* 长双精度浮点数 u */
const npy_longdouble eps = exp2l(-105.); /* 0x1.0000000000000p-105L,用于计算精度 */
GET_LDOUBLE_WORDS64(hx, lx, x); /* 获取长双精度浮点数 x 的整数部分 */
ihx = hx & 0x7fffffffffffffffLL; /* 取 x 的绝对值的整数部分 */
ilx = lx & 0x7fffffffffffffffLL; /* 取 x 的绝对值的低位整数部分 */
if (((ihx & 0x7ff0000000000000LL) == 0x7ff0000000000000LL) &&
((ihx & 0x000fffffffffffffLL) != 0)) {
return x; /* 如果 x 是 NaN,则返回 x */
}
if (ihx == 0 && ilx == 0) { /* 如果 x 等于 0 */
SET_LDOUBLE_WORDS64(x, p, 0ULL); /* 设置 x 为 +-minsubnormal */
u = x * x;
if (u == x) {
return u; /* 如果 u 等于 x,则返回 u */
}
else {
return x; /* 否则返回 x */
}
}
if (p < 0) { /* 如果 p 小于 0 */
if ((hx == 0xffefffffffffffffLL) && (lx == 0xfc8ffffffffffffeLL))
return x + x; /* 如果溢出,返回 -inf */
if (hx >= 0x7ff0000000000000LL) { /* 如果 x 是无穷大 */
SET_LDOUBLE_WORDS64(u, 0x7fefffffffffffffLL, 0x7c8ffffffffffffeLL);
return u; /* 返回特定值 u */
}
if (ihx <= 0x0360000000000000LL) { /* 如果 x 小于等于 LDBL_MIN */
u = math_opt_barrier(x); /* 对 x 进行数学优化屏障 */
x -= LDBL_TRUE_MIN; /* 减去 LDBL_TRUE_MIN */
if (ihx < 0x0360000000000000LL || (hx > 0 && (npy_int64)lx <= 0) ||
(hx < 0 && (npy_int64)lx > 1)) {
u = u * u;
math_force_eval(u); /* 强制评估 u,触发下溢标志 */
}
return x; /* 返回 x */
}
if (ihx < 0x06a0000000000000LL) { /* 如果 ulp 会变成非规格化数 */
SET_LDOUBLE_WORDS64(u, (hx & 0x7ff0000000000000LL), 0ULL);
u *= eps; /* u 乘以 eps */
}
else
SET_LDOUBLE_WORDS64(
u, (hx & 0x7ff0000000000000LL) - 0x0690000000000000LL,
0ULL);
return x - u; /* 返回 x - u */
}
这段代码是关于处理长双精度浮点数的宏定义和函数实现,主要用于浮点数的精确计算和特殊情况处理。
else { /* p >= 0, x += ulp */
if ((hx == 0x7fefffffffffffffLL) && (lx == 0x7c8ffffffffffffeLL))
return x + x; /* overflow, return +inf */
if ((npy_uint64)hx >= 0xfff0000000000000ULL) {
SET_LDOUBLE_WORDS64(u, 0xffefffffffffffffLL, 0xfc8ffffffffffffeLL);
return u;
}
if (ihx <= 0x0360000000000000LL) { /* x <= LDBL_MIN */
u = math_opt_barrier(x);
x += LDBL_TRUE_MIN;
if (ihx < 0x0360000000000000LL ||
(hx > 0 && (npy_int64)lx < 0 && lx != 0x8000000000000001LL) ||
(hx < 0 && (npy_int64)lx >= 0)) {
u = u * u;
math_force_eval(u); /* raise underflow flag */
}
if (x == 0.0L)
x = -0.0L;
return x;
}
if (ihx < 0x06a0000000000000LL) { /* ulp will denormal */
SET_LDOUBLE_WORDS64(u, (hx & 0x7ff0000000000000LL), 0ULL);
u *= eps;
}
else {
SET_LDOUBLE_WORDS64(
u, (hx & 0x7ff0000000000000LL) - 0x0690000000000000LL,
0ULL);
}
return x + u;
}
}
static long double
_next(long double x, int p)
{
volatile npy_longdouble t; // 声明一个 volatile 类型的长双精浮点数 t,用于临时存储计算结果
union IEEEl2bitsrep ux; // 声明一个联合体 ux,用于存储长双精浮点数 x 的位表示
ux.e = x; // 将长双精浮点数 x 的值赋给联合体 ux 的位表示
if ((GET_LDOUBLE_EXP(ux) == 0x7fff &&
((GET_LDOUBLE_MANH(ux) & ~LDBL_NBIT) | GET_LDOUBLE_MANL(ux)) != 0)) {
return ux.e; /* x is nan */ // 如果 x 是 NaN,则直接返回 x
}
if (ux.e == 0.0) {
SET_LDOUBLE_MANH(ux, 0); /* return +-minsubnormal */ // 设置 ux 的尾数部分为 +-minsubnormal
SET_LDOUBLE_MANL(ux, 1); // 将 ux 的尾数部分的低位设置为 1
if (p >= 0) {
SET_LDOUBLE_SIGN(ux, 0); // 如果 p 大于等于 0,则设置 ux 的符号位为正号
}
else {
SET_LDOUBLE_SIGN(ux, 1); // 否则,设置 ux 的符号位为负号
}
t = ux.e * ux.e; // 计算 ux.e 的平方,并将结果存储在 t 中
if (t == ux.e) {
return t; // 如果 t 等于 ux.e,则返回 t
}
else {
return ux.e; /* raise underflow flag */ // 否则,返回 ux.e,并可能引发下溢标志
}
}
if (p < 0) { /* x -= ulp */ // 如果 p 小于 0,则表示 x 减去最后一个有效位
if (GET_LDOUBLE_MANL(ux) == 0) {
if ((GET_LDOUBLE_MANH(ux) & ~LDBL_NBIT) == 0) {
SET_LDOUBLE_EXP(ux, GET_LDOUBLE_EXP(ux) - 1); // 如果 ux 的尾数部分低位为 0,并且尾数部分高位去除掉了 LDBL_NBIT,那么将指数部分减 1
}
SET_LDOUBLE_MANH(ux, (GET_LDOUBLE_MANH(ux) - 1) |
(GET_LDOUBLE_MANH(ux) & LDBL_NBIT)); // 将 ux 的尾数部分高位减 1,并保留 LDBL_NBIT
}
SET_LDOUBLE_MANL(ux, GET_LDOUBLE_MANL(ux) - 1); // 将 ux 的尾数部分低位减 1
}
else { /* x += ulp */ // 否则,表示 x 加上最后一个有效位
SET_LDOUBLE_MANL(ux, GET_LDOUBLE_MANL(ux) + 1); // 将 ux 的尾数部分低位加 1
if (GET_LDOUBLE_MANL(ux) == 0) {
SET_LDOUBLE_MANH(ux, (GET_LDOUBLE_MANH(ux) + 1) |
(GET_LDOUBLE_MANH(ux) & LDBL_NBIT)); // 如果 ux 的尾数部分低位变为 0,将 ux 的尾数部分高位加 1,并保留 LDBL_NBIT
if ((GET_LDOUBLE_MANH(ux) & ~LDBL_NBIT) == 0) {
SET_LDOUBLE_EXP(ux, GET_LDOUBLE_EXP(ux) + 1); // 如果 ux 的尾数部分高位去除掉了 LDBL_NBIT,则将指数部分加 1
}
}
}
if (GET_LDOUBLE_EXP(ux) == 0x7fff) {
return ux.e + ux.e; /* overflow */ // 如果 ux 的指数部分为 0x7fff,则返回 ux.e 的双倍,并可能引发溢出
}
if (GET_LDOUBLE_EXP(ux) == 0) { /* underflow */ // 如果 ux 的指数部分为 0,则表示发生下溢
if (LDBL_NBIT) {
SET_LDOUBLE_MANH(ux, GET_LDOUBLE_MANH(ux) & ~LDBL_NBIT); // 如果 LDBL_NBIT 为真,则清除 ux 的尾数部分高位的 LDBL_NBIT
}
t = ux.e * ux.e;
if (t != ux.e) { /* raise underflow flag */ // 如果 t 不等于 ux.e,则可能引发下溢标志
return ux.e;
}
}
return ux.e; // 返回计算后的长双精浮点数值 ux.e
}
namespace {
template <typename T>
struct numeric_limits;
template <>
struct numeric_limits<float> {
static const npy_float nan; // 定义 float 类型的特殊值 nan
};
const npy_float numeric_limits<float>::nan = NPY_NANF; // 初始化 float 类型的 nan
template <>
struct numeric_limits<double> {
static const npy_double nan; // 定义 double 类型的特殊值 nan
};
const npy_double numeric_limits<double>::nan = NPY_NAN; // 初始化 double 类型的 nan
template <>
struct numeric_limits<long double> {
static const npy_longdouble nan; // 定义 long double 类型的特殊值 nan
};
const npy_longdouble numeric_limits<long double>::nan = NPY_NANL; // 初始化 long double 类型的 nan
} // namespace
template <typename type>
static type
_npy_spacing(type x)
{
/* XXX: npy isnan/isinf may be optimized by bit twiddling */ // 提示可能通过位操作对 npy 的 isnan/isinf 进行优化
if (npy_isinf(x)) {
return numeric_limits<type>::nan; // 如果 x 是无穷大,则返回该类型的 nan 值
}
return _next(x, 1) - x; // 否则,返回 x 的下一个值减去 x 本身的差值
}
/*
* Instantiation of C interface
*/
extern "C" {
npy_float
npy_spacingf(npy_float x)
{
return _npy_spacing(x); // 返回 float 类型的 npy_spacing 函数调用结果
}
npy_double
npy_spacing(npy_double x)
{
return _npy_spacing(x); // 返回 double 类型的 npy_spacing 函数调用结果
}
npy_longdouble
npy_spacingl(npy_longdouble x)
{
return _npy_spacing(x); // 返回 long double 类型的 npy_spacing 函数调用结果
}
}
extern "C" int
npy_clear_floatstatus()
{
char x = 0; // 声明一个 char 类型的变量 x,并初始化为 0
return npy_clear_floatstatus_barrier(&x);
extern "C" int
npy_get_floatstatus()
{
// 定义一个字符变量 x 并初始化为 0
char x = 0;
// 调用 npy_get_floatstatus_barrier 函数,传入 x 的地址作为参数,并返回结果
return npy_get_floatstatus_barrier(&x);
}
/*
* 用于浮点错误处理的通用 C99 代码。这些函数主要是因为 `fenv.h` 在 C89 中未标准化,
* 因此它们提供了更好的可移植性。在 C99/C++11 中应该不再需要这些,可以直接从 `fenv.h` 中使用更多功能。
*/
/*
* 根据 C99 标准,如果 FE_DIVBYZERO 等未支持,则可能不会提供。在这种情况下,NumPy 将无法正确报告这些错误,
* 但我们仍应允许编译(无论测试是否通过)。通过在本地定义它们为 0,我们使它们成为无操作。例如,`musl` 仍定义所有这些函数(作为无操作):
* https://git.musl-libc.org/cgit/musl/tree/src/fenv/fenv.c
* 并在其测试中执行类似的替换:
* http://nsz.repo.hu/git/?p=libc-test;a=blob;f=src/common/mtest.h;h=706c1ba23ea8989b17a2f72ed1a919e187c06b6a;hb=HEAD
*/
extern "C" int
npy_get_floatstatus_barrier(char *param)
{
// 使用 fetestexcept 函数获取当前浮点异常状态并存储在 fpstatus 中
int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW |
FE_INVALID);
/*
* 通过使用 volatile,编译器无法重新排序此调用
*/
// 如果传入的 param 不为 NULL,则声明一个 volatile 的字符变量 c,并使用 param 转换后的指针来初始化它
if (param != NULL) {
volatile char NPY_UNUSED(c) = *(char *)param;
}
// 根据 fpstatus 的位掩码,返回相应的浮点异常错误码
return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) |
((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) |
((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) |
((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0);
}
extern "C" int
npy_clear_floatstatus_barrier(char *param)
{
/* 在 x86 上测试浮点状态比清除状态快 50-100 倍 */
// 调用 npy_get_floatstatus_barrier 函数获取当前浮点异常状态并存储在 fpstatus 中
int fpstatus = npy_get_floatstatus_barrier(param);
// 如果 fpstatus 不为 0,则调用 feclearexcept 函数清除 FE_DIVBYZERO、FE_OVERFLOW、FE_UNDERFLOW 和 FE_INVALID 异常
if (fpstatus != 0) {
feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INVALID);
}
// 返回 fpstatus
return fpstatus;
}
extern "C" void
npy_set_floatstatus_divbyzero(void)
{
// 调用 feraiseexcept 函数,设置 FE_DIVBYZERO 异常
feraiseexcept(FE_DIVBYZERO);
}
extern "C" void
npy_set_floatstatus_overflow(void)
{
// 调用 feraiseexcept 函数,设置 FE_OVERFLOW 异常
feraiseexcept(FE_OVERFLOW);
}
extern "C" void
npy_set_floatstatus_underflow(void)
{
// 调用 feraiseexcept 函数,设置 FE_UNDERFLOW 异常
feraiseexcept(FE_UNDERFLOW);
}
extern "C" void
npy_set_floatstatus_invalid(void)
{
// 调用 feraiseexcept 函数,设置 FE_INVALID 异常
feraiseexcept(FE_INVALID);
}
.\numpy\numpy\_core\src\npymath\npy_math.c
/*
* vim:syntax=c
* This file is compiled into the npy_math library with externally visible
* symbols, and the static and inline specifiers utilized in the npy_math
* function definitions are switched off.
*/
// 设置 C 语法高亮,指定这个文件被编译为 npy_math 库,其中包含外部可见的符号
// 在 npy_math 函数定义中,关闭了 static 和 inline 修饰符的使用
// 包含 npy_math_internal.h 头文件
.\numpy\numpy\_core\src\npymath\npy_math_common.h
/*
* Common headers needed by every npy math compilation unit
*/
.\numpy\numpy\_core\src\npymath\npy_math_private.h
/*
*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
/*
* from: @(
* $FreeBSD$
*/
using std::isgreater;
using std::isless;
/*
* The original fdlibm code used statements like:
* n0 = ((*(int*)&one)>>29)^1; * index of high word *
* ix0 = *(n0+(int*)&x); * high word of x *
* ix1 = *((1-n0)+(int*)&x); * low word of x *
* to dig two 32 bit words out of the 64 bit IEEE floating point
* value. That is non-ANSI, and, moreover, the gcc instruction
* scheduler gets it wrong. We instead use the following macros.
* Unlike the original code, we determine the endianness at compile
* time, not at run time; I don't see much benefit to selecting
* endianness at run time.
*/
/*
* A union which permits us to convert between a double and two 32 bit
* ints.
*/
/* XXX: not really, but we already make this assumption elsewhere. Will have to
* fix this at some point */
#define IEEE_WORD_ORDER NPY_BYTE_ORDER
#if IEEE_WORD_ORDER == NPY_BIG_ENDIAN
typedef union
{
double value;
struct
{
npy_uint32 msw;
npy_uint32 lsw;
} parts;
} ieee_double_shape_type;
#endif
#if IEEE_WORD_ORDER == NPY_LITTLE_ENDIAN
typedef union
{
double value;
struct
{
npy_uint32 lsw;
npy_uint32 msw;
} parts;
} ieee_double_shape_type;
#endif
/* Get two 32 bit ints from a double. */
#define EXTRACT_WORDS(ix0,ix1,d) \
do { \
ieee_double_shape_type ew_u; \
ew_u.value = (d); \
(ix0) = ew_u.parts.msw; \
(ix1) = ew_u.parts.lsw; \
} while (0)
/* Get the more significant 32 bit int from a double. */
#define GET_HIGH_WORD(i,d) \
do { \
ieee_double_shape_type gh_u; \
gh_u.value = (d); \
(i) = gh_u.parts.msw; \
} while (0)
/* Get the less significant 32 bit int from a double. */
#define GET_LOW_WORD(i,d) \
do { \
ieee_double_shape_type gl_u; \
gl_u.value = (d); \
(i) = gl_u.parts.lsw; \
} while (0)
#endif /* _NPY_MATH_PRIVATE_H_ */
/*
* Set the low 32 bits of a double from an int value.
*/
#define SET_LOW_WORD(d,v) \
do { \
ieee_double_shape_type sl_u; \
sl_u.value = (d); \
sl_u.parts.lsw = (v); \
(d) = sl_u.value; \
} while (0)
/*
* Set the high 32 bits of a double from an int value.
*/
#define SET_HIGH_WORD(d,v) \
do { \
ieee_double_shape_type sh_u; \
sh_u.value = (d); \
sh_u.parts.msw = (v); \
(d) = sh_u.value; \
} while (0)
/*
* Set a double value from two 32-bit int values.
*/
#define INSERT_WORDS(d,ix0,ix1) \
do { \
ieee_double_shape_type iw_u; \
iw_u.parts.msw = (ix0); \
iw_u.parts.lsw = (ix1); \
(d) = iw_u.value; \
} while (0)
/*
* A union allowing conversion between a float and a 32-bit int.
*/
typedef union
{
float value;
/* FIXME: Assumes 32 bit int. */
npy_uint32 word;
} ieee_float_shape_type;
/*
* Get a 32-bit int representation of a float value.
*/
#define GET_FLOAT_WORD(i,d) \
do { \
ieee_float_shape_type gf_u; \
gf_u.value = (d); \
(i) = gf_u.word; \
} while (0)
/*
* Set a float value from a 32-bit int representation.
*/
#define SET_FLOAT_WORD(d,i) \
do { \
ieee_float_shape_type sf_u; \
sf_u.word = (i); \
(d) = sf_u.value; \
} while (0)
/*
* Long double support
*/
#if defined(HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE)
/*
* 定义一个类型别名 `IEEEl2bitsrep_part`,用于表示 Intel 扩展的 80 位浮点数的部分位表示
* Bit representation is
* | junk | s |eeeeeeeeeeeeeee|mmmmmmmm................mmmmmmm|
* | 16 bits| 1 bit | 15 bits | 64 bits |
* | a[2] | a[1] | a[0] |
*
* 16 位 a[2] 中的更强的位是垃圾位
*/
typedef npy_uint32 IEEEl2bitsrep_part;
union IEEEl2bitsrep {
npy_longdouble e; // 用长双精度浮点数表示的 IEEE 80 位浮点数
IEEEl2bitsrep_part a[3]; // 用于访问 80 位浮点数各个部分的数组表示
};
#define LDBL_MANL_INDEX 0 // 低位部分在 a 数组中的索引
#define LDBL_MANL_MASK 0xFFFFFFFF // 低位部分的掩码
#define LDBL_MANL_SHIFT 0 // 低位部分的位移
#define LDBL_MANH_INDEX 1 // 高位部分在 a 数组中的索引
#define LDBL_MANH_MASK 0xFFFFFFFF // 高位部分的掩码
#define LDBL_MANH_SHIFT 0 // 高位部分的位移
#define LDBL_EXP_INDEX 2 // 指数部分在 a 数组中的索引
#define LDBL_EXP_MASK 0x7FFF // 指数部分的掩码
#define LDBL_EXP_SHIFT 0 // 指数部分的位移
#define LDBL_SIGN_INDEX 2 // 符号位在 a 数组中的索引
#define LDBL_SIGN_MASK 0x8000 // 符号位的掩码
#define LDBL_SIGN_SHIFT 15 // 符号位的位移
#define LDBL_NBIT 0x80000000 // 用于检测 NaN 的位掩码
typedef npy_uint32 ldouble_man_t; // 用于表示长双精度浮点数低位部分的类型别名
typedef npy_uint32 ldouble_exp_t; // 用于表示长双精度浮点数指数部分的类型别名
typedef npy_uint32 ldouble_sign_t; // 用于表示长双精度浮点数符号部分的类型别名
#elif defined(HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE)
/*
* Intel extended 80 bits precision, 16 bytes alignment.. Bit representation is
* | junk | s |eeeeeeeeeeeeeee|mmmmmmmm................mmmmmmm|
* | 16 bits| 1 bit | 15 bits | 64 bits |
* | a[2] | a[1] | a[0] |
*
* a[3] and 16 stronger bits of a[2] are junk
*/
typedef npy_uint32 IEEEl2bitsrep_part; // 定义IEEE长双精度类型的部分位表示
union IEEEl2bitsrep {
npy_longdouble e; // 使用长双精度类型的联合表示
IEEEl2bitsrep_part a[4]; // 使用四个32位整数数组表示长双精度数的位
};
#define LDBL_MANL_INDEX 0 // 长双精度数低位的索引
#define LDBL_MANL_MASK 0xFFFFFFFF // 长双精度数低位的掩码
#define LDBL_MANL_SHIFT 0 // 长双精度数低位的位移量
#define LDBL_MANH_INDEX 1 // 长双精度数高位的索引
#define LDBL_MANH_MASK 0xFFFFFFFF // 长双精度数高位的掩码
#define LDBL_MANH_SHIFT 0 // 长双精度数高位的位移量
#define LDBL_EXP_INDEX 2 // 长双精度数指数部分的索引
#define LDBL_EXP_MASK 0x7FFF // 长双精度数指数部分的掩码
#define LDBL_EXP_SHIFT 0 // 长双精度数指数部分的位移量
#define LDBL_SIGN_INDEX 2 // 长双精度数符号位的索引
#define LDBL_SIGN_MASK 0x8000 // 长双精度数符号位的掩码
#define LDBL_SIGN_SHIFT 15 // 长双精度数符号位的位移量
#define LDBL_NBIT 0x800000000 // 长双精度数N位的掩码
typedef npy_uint32 ldouble_man_t; // 长双精度数尾数的类型定义
typedef npy_uint32 ldouble_exp_t; // 长双精度数指数的类型定义
typedef npy_uint32 ldouble_sign_t; // 长双精度数符号的类型定义
#elif defined(HAVE_LDOUBLE_MOTOROLA_EXTENDED_12_BYTES_BE)
/*
* Motorola extended 80 bits precision. Bit representation is
* | s |eeeeeeeeeeeeeee| junk |mmmmmmmm................mmmmmmm|
* | 1 bit | 15 bits | 16 bits| 64 bits |
* | a[0] | a[1] | a[2] |
*
* 16 low bits of a[0] are junk
*/
typedef npy_uint32 IEEEl2bitsrep_part; // 定义IEEE长双精度类型的部分位表示
union IEEEl2bitsrep {
npy_longdouble e; // 使用长双精度类型的联合表示
IEEEl2bitsrep_part a[3]; // 使用三个32位整数数组表示长双精度数的位
};
#define LDBL_MANL_INDEX 2 // 长双精度数低位的索引
#define LDBL_MANL_MASK 0xFFFFFFFF // 长双精度数低位的掩码
#define LDBL_MANL_SHIFT 0 // 长双精度数低位的位移量
#define LDBL_MANH_INDEX 1 // 长双精度数高位的索引
#define LDBL_MANH_MASK 0xFFFFFFFF // 长双精度数高位的掩码
#define LDBL_MANH_SHIFT 0 // 长双精度数高位的位移量
#define LDBL_EXP_INDEX 0 // 长双精度数指数部分的索引
#define LDBL_EXP_MASK 0x7FFF0000 // 长双精度数指数部分的掩码
#define LDBL_EXP_SHIFT 16 // 长双精度数指数部分的位移量
#define LDBL_SIGN_INDEX 0 // 长双精度数符号位的索引
#define LDBL_SIGN_MASK 0x80000000 // 长双精度数符号位的掩码
#define LDBL_SIGN_SHIFT 31 // 长双精度数符号位的位移量
#define LDBL_NBIT 0x80000000 // 长双精度数N位的掩码
typedef npy_uint32 ldouble_man_t; // 长双精度数尾数的类型定义
typedef npy_uint32 ldouble_exp_t; // 长双精度数指数的类型定义
typedef npy_uint32 ldouble_sign_t; // 长双精度数符号的类型定义
#elif defined(HAVE_LDOUBLE_IEEE_DOUBLE_BE)
/* 64 bits IEEE double precision aligned on 16 bytes: used by ppc arch on
* Mac OS X */
/*
* IEEE double precision. Bit representation is
* | s |eeeeeeeeeee|mmmmmmmm................mmmmmmm|
* |1 bit| 11 bits | 52 bits |
* | a[0] | a[1] |
*/
typedef npy_uint32 IEEEl2bitsrep_part; // 定义IEEE长双精度类型的部分位表示
union IEEEl2bitsrep {
npy_longdouble e; // 使用长双精度类型的联合表示
IEEEl2bitsrep_part a[2]; // 使用两个32位整数数组表示长双精度数的位
};
#define LDBL_MANL_INDEX 1 // 长双精度数低位的索引
# 定义长双精度浮点数的尾数掩码和位移量
# 尾数掩码,用于提取尾数部分
# 尾数位移量,用于右移操作提取尾数
# 定义长双精度浮点数的指数索引、指数掩码和位移量
# 指数索引,指数掩码用于提取指数部分
# 指数掩码,用于提取指数部分
# 指数位移量,用于右移操作提取指数部分
# 定义长双精度浮点数的符号索引、符号掩码和位移量
# 符号索引,符号掩码用于提取符号部分
# 符号掩码,用于提取符号部分
# 符号位移量,用于右移操作提取符号部分
# 定义长双精度浮点数的最低有效位
typedef npy_uint32 ldouble_man_t; // 定义长双精度浮点数的尾数类型
typedef npy_uint32 ldouble_exp_t; // 定义长双精度浮点数的指数类型
typedef npy_uint32 ldouble_sign_t; // 定义长双精度浮点数的符号类型
#elif defined(HAVE_LDOUBLE_IEEE_DOUBLE_LE)
/*
* 64 bits IEEE double precision, Little Endian.
*
* IEEE double precision. Bit representation is
* | s |eeeeeeeeeee|mmmmmmmm................mmmmmmm|
* |1 bit| 11 bits | 52 bits |
* | a[1] | a[0] |
*/
// 定义结构体,用于存储 IEEE 双精度浮点数的位表示
typedef npy_uint32 IEEEl2bitsrep_part;
// 联合体,允许以多种方式访问同一存储位置的数据
union IEEEl2bitsrep {
npy_longdouble e; // 长双精度浮点数
IEEEl2bitsrep_part a[2]; // 用两个 32 位整数数组表示的位表示
};
// 定义尾数低位索引、掩码和位移量
#define LDBL_MANL_INDEX 0
#define LDBL_MANL_MASK 0xFFFFFFFF
#define LDBL_MANL_SHIFT 0
// 定义尾数高位索引、掩码和位移量
#define LDBL_MANH_INDEX 1
#define LDBL_MANH_MASK 0x000FFFFF
#define LDBL_MANH_SHIFT 0
// 定义指数索引、掩码和位移量
#define LDBL_EXP_INDEX 1
#define LDBL_EXP_MASK 0x7FF00000
#define LDBL_EXP_SHIFT 20
// 定义符号索引、掩码和位移量
#define LDBL_SIGN_INDEX 1
#define LDBL_SIGN_MASK 0x80000000
#define LDBL_SIGN_SHIFT 31
// 定义 NBIT 常量
#define LDBL_NBIT 0x00000080
// 定义长双精度浮点数的尾数、指数和符号类型
typedef npy_uint32 ldouble_man_t;
typedef npy_uint32 ldouble_exp_t;
typedef npy_uint32 ldouble_sign_t;
#define LDBL_EXP_MASK 0x7FFF000000000000
#define LDBL_EXP_SHIFT 48
# 定义长双精度浮点数的指数掩码和指数位移量
# LDBL_EXP_MASK: 用于屏蔽长双精度浮点数的指数部分的掩码
# LDBL_EXP_SHIFT: 指数部分在长双精度浮点数中的位移量
#define LDBL_SIGN_INDEX 1
#define LDBL_SIGN_MASK 0x8000000000000000
#define LDBL_SIGN_SHIFT 63
# 定义长双精度浮点数的符号索引、符号掩码和符号位移量
# LDBL_SIGN_INDEX: 符号位在长双精度浮点数中的索引位置
# LDBL_SIGN_MASK: 用于屏蔽长双精度浮点数的符号位的掩码
# LDBL_SIGN_SHIFT: 符号位在长双精度浮点数中的位移量
#define LDBL_NBIT 0
# 定义长双精度浮点数的第一位(最高位)
# LDBL_NBIT: 长双精度浮点数的最高位(最高有效位)
typedef npy_uint64 ldouble_man_t;
typedef npy_uint64 ldouble_exp_t;
typedef npy_uint32 ldouble_sign_t;
# 声明长双精度浮点数的尾数、指数和符号的数据类型
# ldouble_man_t: 长双精度浮点数的尾数类型,为 64 位无符号整数
# ldouble_exp_t: 长双精度浮点数的指数类型,为 64 位无符号整数
# ldouble_sign_t: 长双精度浮点数的符号类型,为 32 位无符号整数
#if !defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE) && \
!defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE)
/* Get the sign bit of x. x should be of type IEEEl2bitsrep */
#define GET_LDOUBLE_SIGN(x) \
(((x).a[LDBL_SIGN_INDEX] & LDBL_SIGN_MASK) >> LDBL_SIGN_SHIFT)
/* Set the sign bit of x to v. x should be of type IEEEl2bitsrep */
#define SET_LDOUBLE_SIGN(x, v) \
((x).a[LDBL_SIGN_INDEX] = \
((x).a[LDBL_SIGN_INDEX] & ~LDBL_SIGN_MASK) | \
(((IEEEl2bitsrep_part)(v) << LDBL_SIGN_SHIFT) & LDBL_SIGN_MASK))
/* Get the exp bits of x. x should be of type IEEEl2bitsrep */
#define GET_LDOUBLE_EXP(x) \
(((x).a[LDBL_EXP_INDEX] & LDBL_EXP_MASK) >> LDBL_EXP_SHIFT)
/* Set the exp bit of x to v. x should be of type IEEEl2bitsrep */
#define SET_LDOUBLE_EXP(x, v) \
((x).a[LDBL_EXP_INDEX] = \
((x).a[LDBL_EXP_INDEX] & ~LDBL_EXP_MASK) | \
(((IEEEl2bitsrep_part)(v) << LDBL_EXP_SHIFT) & LDBL_EXP_MASK))
/* Get the manl bits of x. x should be of type IEEEl2bitsrep */
#define GET_LDOUBLE_MANL(x) \
(((x).a[LDBL_MANL_INDEX] & LDBL_MANL_MASK) >> LDBL_MANL_SHIFT)
/* Set the manl bit of x to v. x should be of type IEEEl2bitsrep */
#define SET_LDOUBLE_MANL(x, v) \
((x).a[LDBL_MANL_INDEX] = \
((x).a[LDBL_MANL_INDEX] & ~LDBL_MANL_MASK) | \
(((IEEEl2bitsrep_part)(v) << LDBL_MANL_SHIFT) & LDBL_MANL_MASK))
/* Get the manh bits of x. x should be of type IEEEl2bitsrep */
#define GET_LDOUBLE_MANH(x) \
(((x).a[LDBL_MANH_INDEX] & LDBL_MANH_MASK) >> LDBL_MANH_SHIFT)
/* Set the manh bit of x to v. x should be of type IEEEl2bitsrep */
#define SET_LDOUBLE_MANH(x, v) \
((x).a[LDBL_MANH_INDEX] = \
((x).a[LDBL_MANH_INDEX] & ~LDBL_MANH_MASK) | \
(((IEEEl2bitsrep_part)(v) << LDBL_MANH_SHIFT) & LDBL_MANH_MASK))
#endif /* !HAVE_LDOUBLE_DOUBLE_DOUBLE_* */
#endif /* !_NPY_MATH_PRIVATE_H_ */
/*
These macros define operations to manipulate specific bits within a structure
representing a long double's internal bit layout. They are used when certain
configurations for IBM's double-double long double format are not defined.
*/
/*
GET_LDOUBLE_SIGN(x): Retrieves the sign bit from the IEEEl2bitsrep structure x.
SET_LDOUBLE_SIGN(x, v): Sets the sign bit of x to the value v (0 or 1).
GET_LDOUBLE_EXP(x): Retrieves the exponent bits from the IEEEl2bitsrep structure x.
SET_LDOUBLE_EXP(x, v): Sets the exponent bits of x to the value v.
GET_LDOUBLE_MANL(x): Retrieves the lower part of the mantissa from the IEEEl2bitsrep structure x.
SET_LDOUBLE_MANL(x, v): Sets the lower part of the mantissa of x to the value v.
GET_LDOUBLE_MANH(x): Retrieves the higher part of the mantissa from the IEEEl2bitsrep structure x.
SET_LDOUBLE_MANH(x, v): Sets the higher part of the mantissa of x to the value v.
*/
/*
The #endif directives close conditional compilation blocks, checking for specific configurations
of IBM's double-double long double format and preventing redefinition of these macros when those
configurations are defined. The final
*/
/*
The final
is defined, ensuring proper inclusion of content based on preprocessor directives.
*/
.\numpy\numpy\_core\src\npysort\binsearch.cpp
/* -*- c -*- */
// 禁用已弃用的 NumPy API
// 引入 NumPy 的数组类型和通用头文件
// 引入排序和二分搜索相关的自定义头文件
// 引入标准库头文件
// binsearch 函数的搜索变体的枚举器
enum arg_t
{
noarg, // 无参数
arg // 有参数
};
enum side_t
{
left, // 左侧
right // 右侧
};
// 将枚举器映射到比较器的模板
template <class Tag, side_t side>
struct side_to_cmp;
// 左侧搜索比较器的特化
template <class Tag>
struct side_to_cmp<Tag, left> {
static constexpr auto value = Tag::less; // 使用 Tag 的 less 函数对象
};
// 右侧搜索比较器的特化
template <class Tag>
struct side_to_cmp<Tag, right> {
static constexpr auto value = Tag::less_equal; // 使用 Tag 的 less_equal 函数对象
};
// 将搜索方向映射到通用比较器的模板
template <side_t side>
struct side_to_generic_cmp;
// 左侧搜索通用比较器的特化
template <>
struct side_to_generic_cmp<left> {
using type = std::less<int>; // 使用 std::less 进行整数比较
};
// 右侧搜索通用比较器的特化
template <>
struct side_to_generic_cmp<right> {
using type = std::less_equal<int>; // 使用 std::less_equal 进行整数比较
};
/*
*****************************************************************************
** NUMERIC SEARCHES **
*****************************************************************************
*/
// 泛型二分搜索函数的实现
template <class Tag, side_t side>
static void
binsearch(const char *arr, const char *key, char *ret, npy_intp arr_len,
npy_intp key_len, npy_intp arr_str, npy_intp key_str,
npy_intp ret_str, PyArrayObject *)
{
using T = typename Tag::type; // 使用 Tag 的类型 T
auto cmp = side_to_cmp<Tag, side>::value; // 获取比较器函数对象
npy_intp min_idx = 0;
npy_intp max_idx = arr_len;
T last_key_val;
if (key_len == 0) {
return; // 如果 key 长度为 0,则直接返回
}
last_key_val = *(const T *)key; // 获取第一个 key 的值
for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
const T key_val = *(const T *)key; // 获取当前 key 的值
/*
* 根据前一个 key 值更新索引,当 key 是有序的时候能显著提升搜索效率,
* 但对于纯随机的情况略微降低速度。
*/
if (cmp(last_key_val, key_val)) {
max_idx = arr_len; // 如果上一个 key 小于当前 key,则更新最大索引
}
else {
min_idx = 0; // 否则重置最小索引
max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len; // 更新最大索引
}
last_key_val = key_val; // 更新上一个 key 的值为当前 key
while (min_idx < max_idx) {
const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1); // 计算中间索引
const T mid_val = *(const T *)(arr + mid_idx * arr_str); // 获取中间值
if (cmp(mid_val, key_val)) {
min_idx = mid_idx + 1; // 更新最小索引
}
else {
max_idx = mid_idx; // 更新最大索引
}
}
*(npy_intp *)ret = min_idx; // 将结果存入 ret 中
}
}
// 带参数的泛型二分搜索函数的实现
template <class Tag, side_t side>
static int
argbinsearch(const char *arr, const char *key, const char *sort, char *ret,
npy_intp arr_len, npy_intp key_len, npy_intp arr_str,
npy_intp key_str, npy_intp sort_str, npy_intp ret_str,
PyArrayObject *)
{
using T = typename Tag::type; // 使用 Tag 的类型 T
auto cmp = side_to_cmp<Tag, side>::value; // 获取比较器函数对象
npy_intp min_idx = 0; // 初始化最小索引为0,表示搜索范围的起始位置
npy_intp max_idx = arr_len; // 初始化最大索引为数组长度,表示搜索范围的结束位置
T last_key_val; // 声明变量用于存储上一个比较的关键值
if (key_len == 0) {
return 0; // 如果关键字长度为0,直接返回0
}
last_key_val = *(const T *)key; // 获取关键字的第一个元素作为初始的上一个关键值
for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
const T key_val = *(const T *)key; // 获取当前关键字的值
/*
* 根据前一个关键字值更新索引范围,
* 当关键字按顺序排列时,这样做可以显著提升搜索效率,
* 但是对于完全随机的关键字会略微减慢速度。
*/
if (cmp(last_key_val, key_val)) {
max_idx = arr_len; // 如果当前关键字与上一个不同,则更新最大索引为数组长度
}
else {
min_idx = 0; // 如果当前关键字与上一个相同,则重置最小索引为0
max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len; // 并增加最大索引,但不超过数组长度
}
last_key_val = key_val; // 更新上一个关键值为当前关键值
while (min_idx < max_idx) {
const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1); // 计算中间索引位置
const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx * sort_str); // 获取排序后的索引值
T mid_val;
if (sort_idx < 0 || sort_idx >= arr_len) {
return -1; // 如果排序索引超出有效范围,返回错误标志
}
mid_val = *(const T *)(arr + sort_idx * arr_str); // 获取数组中排序后位置的值
if (cmp(mid_val, key_val)) {
min_idx = mid_idx + 1; // 如果中间值小于关键值,更新最小索引为中间索引加一
}
else {
max_idx = mid_idx; // 否则更新最大索引为中间索引
}
}
*(npy_intp *)ret = min_idx; // 将最小索引值写入返回值中
}
return 0; // 返回成功标志
/*
* 使用模板函数实现二分查找算法,查找指定键在排序数组中的位置
* 参数说明:
* - arr: 输入数组的起始地址
* - key: 要查找的键的起始地址
* - ret: 返回的位置的地址
* - arr_len: 数组的长度
* - key_len: 键的长度(假设是键的长度,可能是用来迭代的计数器)
* - arr_str: 数组中每个元素的步长(假设是元素之间的偏移量)
* - key_str: 键中每个字符的步长(假设是字符之间的偏移量)
* - ret_str: 返回位置指针每次迭代时的步长
* - cmp: 用于比较元素的对象
*/
template <side_t side>
static void
npy_binsearch(const char *arr, const char *key, char *ret, npy_intp arr_len,
npy_intp key_len, npy_intp arr_str, npy_intp key_str,
npy_intp ret_str, PyArrayObject *cmp)
{
// 使用模板元编程,根据边界方向选择相应的比较器类型
using Cmp = typename side_to_generic_cmp<side>::type;
// 获取用于比较元素的函数指针
PyArray_CompareFunc *compare = PyDataType_GetArrFuncs(PyArray_DESCR(cmp))->compare;
// 初始化二分查找的起始和结束位置
npy_intp min_idx = 0;
npy_intp max_idx = arr_len;
// 记录上一个键的位置
const char *last_key = key;
// 循环遍历每个键
for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
/*
* 当前键与上一个键进行比较,根据比较结果更新搜索范围的下界和上界
* 当键有序时,根据上一个键的比较结果更新搜索范围,优化查找速度;
* 当键是随机的时,可能会轻微减慢速度。
*/
if (Cmp{}(compare(last_key, key, cmp), 0)) {
max_idx = arr_len;
}
else {
min_idx = 0;
max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
}
// 更新上一个键的位置
last_key = key;
// 二分查找算法
while (min_idx < max_idx) {
const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
const char *arr_ptr = arr + mid_idx * arr_str;
// 根据比较结果更新搜索范围
if (Cmp{}(compare(arr_ptr, key, cmp), 0)) {
min_idx = mid_idx + 1;
}
else {
max_idx = mid_idx;
}
}
// 将当前位置保存到返回位置的指针中
*(npy_intp *)ret = min_idx;
}
}
/*
* 使用模板函数实现二分查找算法,查找指定键在排序数组中的位置
* 参数说明:
* - arr: 输入数组的起始地址
* - key: 要查找的键的起始地址
* - sort: 排序数组的起始地址(假设是排序数组,可能用于比较)
* - ret: 返回的位置的地址
* - arr_len: 数组的长度
* - key_len: 键的长度(假设是键的长度,可能是用来迭代的计数器)
* - arr_str: 数组中每个元素的步长(假设是元素之间的偏移量)
* - key_str: 键中每个字符的步长(假设是字符之间的偏移量)
* - sort_str: 排序数组中每个元素的步长(假设是元素之间的偏移量)
* - ret_str: 返回位置指针每次迭代时的步长
* - cmp: 用于比较元素的对象
* 返回值:
* - 返回找到的位置的索引,或者是否找到(假设是成功返回索引,失败返回-1)
*/
template <side_t side>
static int
npy_argbinsearch(const char *arr, const char *key, const char *sort, char *ret,
npy_intp arr_len, npy_intp key_len, npy_intp arr_str,
npy_intp key_str, npy_intp sort_str, npy_intp ret_str,
PyArrayObject *cmp)
{
// 使用模板元编程,根据边界方向选择相应的比较器类型
using Cmp = typename side_to_generic_cmp<side>::type;
// 获取用于比较元素的函数指针
PyArray_CompareFunc *compare = PyDataType_GetArrFuncs(PyArray_DESCR(cmp))->compare;
// 初始化二分查找的起始和结束位置
npy_intp min_idx = 0;
npy_intp max_idx = arr_len;
// 记录上一个键的位置
const char *last_key = key;
// 循环遍历每个键
for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
/*
* 当前键与上一个键进行比较,根据比较结果更新搜索范围的下界和上界
* 当键有序时,根据上一个键的比较结果更新搜索范围,优化查找速度;
* 当键是随机的时,可能会轻微减慢速度。
*/
if (Cmp{}(compare(last_key, key, cmp), 0)) {
max_idx = arr_len;
}
else {
min_idx = 0;
max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
}
// 更新上一个键的位置
last_key = key;
// 二分查找算法
while (min_idx < max_idx) {
const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
const char *arr_ptr = arr + mid_idx * arr_str;
// 根据比较结果更新搜索范围
if (Cmp{}(compare(arr_ptr, key, cmp), 0)) {
min_idx = mid_idx + 1;
}
else {
max_idx = mid_idx;
}
}
// 将当前位置保存到返回位置的指针中
*(npy_intp *)ret = min_idx;
}
// 返回找到的位置的索引,或者是否找到(假设是成功返回索引,失败返回-1)
return (min_idx < arr_len && compare(arr + min_idx * arr_str, key, cmp) == 0) ? min_idx : -1;
}
for (; key_len > 0; key_len--, key += key_str, ret += ret_str) {
/*
* 根据前一个键的比较结果更新索引,当键有序时,可以显著提升搜索速度,
* 但对于完全随机的键略微降低速度。
*/
// 根据最后一个键与当前键的比较结果,更新最大索引
if (Cmp{}(compare(last_key, key, cmp), 0)) {
max_idx = arr_len;
}
else {
min_idx = 0;
max_idx = (max_idx < arr_len) ? (max_idx + 1) : arr_len;
}
// 更新最后一个键为当前键
last_key = key;
// 二分查找在排序数组中定位键的位置
while (min_idx < max_idx) {
// 计算中间索引
const npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
// 获取排序索引
const npy_intp sort_idx = *(npy_intp *)(sort + mid_idx * sort_str);
const char *arr_ptr;
// 检查排序索引是否有效
if (sort_idx < 0 || sort_idx >= arr_len) {
return -1;
}
// 获取数组中的指针
arr_ptr = arr + sort_idx * arr_str;
// 比较数组中的值和当前键的大小
if (Cmp{}(compare(arr_ptr, key, cmp), 0)) {
// 更新最小索引以查找更大的键
min_idx = mid_idx + 1;
}
else {
// 更新最大索引以查找更小的键
max_idx = mid_idx;
}
}
// 将搜索结果写入返回数组中
*(npy_intp *)ret = min_idx;
}
// 返回成功标志
return 0;
/*
*****************************************************************************
** GENERATOR **
*****************************************************************************
*/
// 模板声明:二分搜索基类
template <arg_t arg>
struct binsearch_base;
// 特化模板:针对具有参数的情况(arg != noarg)
template <>
struct binsearch_base<arg> {
// 定义函数指针类型
using function_type = PyArray_ArgBinSearchFunc *;
// 值类型结构体定义
struct value_type {
int typenum; // 类型编号
function_type binsearch[NPY_NSEARCHSIDES]; // 二分搜索函数指针数组
};
// 生成二分搜索映射表的静态方法
template <class... Tags>
static constexpr std::array<value_type, sizeof...(Tags)>
make_binsearch_map(npy::taglist<Tags...>)
{
// 返回由标签列表生成的值类型数组
return std::array<value_type, sizeof...(Tags)>{
value_type{Tags::type_value,
{(function_type)&argbinsearch<Tags, left>,
(function_type)argbinsearch<Tags, right>}}...};
}
// 静态成员:无参数的二分搜索函数指针数组
static constexpr std::array<function_type, 2> npy_map = {
(function_type)&npy_argbinsearch<left>,
(function_type)&npy_argbinsearch<right>};
};
// 初始化静态成员:无参数的二分搜索函数指针数组
constexpr std::array<binsearch_base<arg>::function_type, 2>
binsearch_base<arg>::npy_map;
// 特化模板:针对无参数的情况(arg == noarg)
template <>
struct binsearch_base<noarg> {
// 定义函数指针类型
using function_type = PyArray_BinSearchFunc *;
// 值类型结构体定义
struct value_type {
int typenum; // 类型编号
function_type binsearch[NPY_NSEARCHSIDES]; // 二分搜索函数指针数组
};
// 生成二分搜索映射表的静态方法
template <class... Tags>
static constexpr std::array<value_type, sizeof...(Tags)>
make_binsearch_map(npy::taglist<Tags...>)
{
// 返回由标签列表生成的值类型数组
return std::array<value_type, sizeof...(Tags)>{
value_type{Tags::type_value,
{(function_type)&binsearch<Tags, left>,
(function_type)binsearch<Tags, right>}}...};
}
// 静态成员:有参数的二分搜索函数指针数组
static constexpr std::array<function_type, 2> npy_map = {
(function_type)&npy_binsearch<left>,
(function_type)&npy_binsearch<right>};
};
// 初始化静态成员:有参数的二分搜索函数指针数组
constexpr std::array<binsearch_base<noarg>::function_type, 2>
binsearch_base<noarg>::npy_map;
// 处理所有二分搜索变体的生成
template <arg_t arg>
struct binsearch_t : binsearch_base<arg> {
// 继承基类的 make_binsearch_map 方法
using binsearch_base<arg>::make_binsearch_map;
// 定义值类型为基类的值类型
using value_type = typename binsearch_base<arg>::value_type;
// 定义标签列表类型
using taglist = npy::taglist<
/* If adding new types, make sure to keep them ordered by type num
*/
npy::bool_tag, npy::byte_tag, npy::ubyte_tag, npy::short_tag,
npy::ushort_tag, npy::int_tag, npy::uint_tag, npy::long_tag,
npy::ulong_tag, npy::longlong_tag, npy::ulonglong_tag,
npy::float_tag, npy::double_tag, npy::longdouble_tag,
npy::cfloat_tag, npy::cdouble_tag, npy::clongdouble_tag,
npy::datetime_tag, npy::timedelta_tag, npy::half_tag>;
// 静态成员:标签列表映射表
static constexpr std::array<value_type, taglist::size> map =
make_binsearch_map(taglist());
};
// 模板声明:二分搜索变体
template <arg_t arg>
/*
* 用于存储二进制搜索函数的映射表,具体类型和大小由模板参数arg决定
*/
constexpr std::array<typename binsearch_t<arg>::value_type,
binsearch_t<arg>::taglist::size>
binsearch_t<arg>::map;
/*
* 获取特定类型arg的二进制搜索函数,根据dtype和side参数返回相应的函数指针
*/
template <arg_t arg>
static inline typename binsearch_t<arg>::function_type
_get_binsearch_func(PyArray_Descr *dtype, NPY_SEARCHSIDE side)
{
// 使用binsearch_t<arg>简化类型名称
using binsearch = binsearch_t<arg>;
// 获取映射表的大小
npy_intp nfuncs = binsearch::map.size();
// 初始化搜索范围的下界和上界
npy_intp min_idx = 0;
npy_intp max_idx = nfuncs;
// 获取dtype的类型编号
int type = dtype->type_num;
// 检查side参数是否有效,若无效则返回NULL
if ((int)side >= (int)NPY_NSEARCHSIDES) {
return NULL;
}
/*
* 使用二分搜索算法查找符合dtype类型的二进制搜索函数
*/
while (min_idx < max_idx) {
// 计算中间索引
npy_intp mid_idx = min_idx + ((max_idx - min_idx) >> 1);
// 根据类型编号比较映射表中的元素,调整搜索范围
if (binsearch::map[mid_idx].typenum < type) {
min_idx = mid_idx + 1;
}
else {
max_idx = mid_idx;
}
}
// 如果找到对应dtype类型的函数,则返回该函数指针
if (min_idx < nfuncs && binsearch::map[min_idx].typenum == type) {
return binsearch::map[min_idx].binsearch[side];
}
// 若未找到对应dtype类型的函数,且dtype具有比较功能,则返回通用的二进制搜索函数
if (PyDataType_GetArrFuncs(dtype)->compare) {
return binsearch::npy_map[side];
}
// 若以上条件都不满足,则返回NULL
return NULL;
}
/*
*****************************************************************************
** C INTERFACE **
*****************************************************************************
*/
extern "C" {
// 返回无参数版本(noarg)的二进制搜索函数
NPY_NO_EXPORT PyArray_BinSearchFunc *
get_binsearch_func(PyArray_Descr *dtype, NPY_SEARCHSIDE side)
{
return _get_binsearch_func<noarg>(dtype, side);
}
// 返回带参数版本(arg)的二进制搜索函数
NPY_NO_EXPORT PyArray_ArgBinSearchFunc *
get_argbinsearch_func(PyArray_Descr *dtype, NPY_SEARCHSIDE side)
{
return _get_binsearch_func<arg>(dtype, side);
}
}
.\numpy\numpy\_core\src\npysort\heapsort.cpp
/*
* The purpose of this module is to add faster sort functions
* that are type-specific. This is done by altering the
* function table for the builtin descriptors.
*
* These sorting functions are copied almost directly from numarray
* with a few modifications (complex comparisons compare the imaginary
* part if the real parts are equal, for example), and the names
* are changed.
*
* The original sorting code is due to Charles R. Harris who wrote
* it for numarray.
*/
/*
* Quick sort is usually the fastest, but the worst case scenario can
* be slower than the merge and heap sorts. The merge sort requires
* extra memory and so for large arrays may not be useful.
*
* The merge sort is *stable*, meaning that equal components
* are unmoved from their entry versions, so it can be used to
* implement lexicographic sorting on multiple keys.
*
* The heap sort is included for completeness.
*/
/*
*****************************************************************************
** GENERIC SORT **
*****************************************************************************
*/
NPY_NO_EXPORT int
npy_heapsort(void *start, npy_intp num, void *varr)
{
PyArrayObject *arr = (PyArrayObject *)varr; /* 将输入转换为 PyArrayObject 类型 */
npy_intp elsize = PyArray_ITEMSIZE(arr); /* 获取数组元素的大小 */
PyArray_CompareFunc *cmp = PyDataType_GetArrFuncs(PyArray_DESCR(arr))->compare; /* 获取比较函数 */
if (elsize == 0) {
return 0; /* 如果元素大小为0,无需排序 */
}
char *tmp = (char *)malloc(elsize); /* 分配临时空间 */
char *a = (char *)start - elsize; /* 起始位置 */
npy_intp i, j, l;
if (tmp == NULL) {
return -NPY_ENOMEM; /* 如果内存分配失败,返回内存不足错误 */
}
for (l = num >> 1; l > 0; --l) {
GENERIC_COPY(tmp, a + l * elsize, elsize); /* 复制元素到临时空间 */
for (i = l, j = l << 1; j <= num;) {
if (j < num &&
cmp(a + j * elsize, a + (j + 1) * elsize, arr) < 0) {
++j; /* 如果右边元素比较大,移动到下一个 */
}
if (cmp(tmp, a + j * elsize, arr) < 0) {
GENERIC_COPY(a + i * elsize, a + j * elsize, elsize); /* 复制元素 */
i = j; /* 更新位置 */
j += j; /* 增加步长 */
}
else {
break; /* 跳出循环 */
}
}
GENERIC_COPY(a + i * elsize, tmp, elsize); /* 复制元素 */
}
// 循环执行直到 num 变为 1
for (; num > 1;) {
// 将 a[num * elsize] 处的元素复制到 tmp 中
GENERIC_COPY(tmp, a + num * elsize, elsize);
// 将 a[elsize] 处的元素复制到 a[num * elsize] 处
GENERIC_COPY(a + num * elsize, a + elsize, elsize);
// num 减少 1
num -= 1;
// 开始堆排序的调整过程
for (i = 1, j = 2; j <= num;) {
// 比较 a[j * elsize] 和 a[(j + 1) * elsize],选择较大的
if (j < num &&
cmp(a + j * elsize, a + (j + 1) * elsize, arr) < 0) {
++j;
}
// 如果 tmp 小于 a[j * elsize],则进行元素交换
if (cmp(tmp, a + j * elsize, arr) < 0) {
GENERIC_COPY(a + i * elsize, a + j * elsize, elsize);
i = j;
j += j; // j 的增加方式有误,应该为 j *= 2
}
else {
break;
}
}
// 将 tmp 复制到 a[i * elsize] 处
GENERIC_COPY(a + i * elsize, tmp, elsize);
}
// 释放 tmp 所占用的内存
free(tmp);
// 返回 0 表示排序完成
return 0;
}
NPY_NO_EXPORT int
npy_aheapsort(void *vv, npy_intp *tosort, npy_intp n, void *varr)
{
// 将输入指针 vv 转换为 char 类型,这样可以按字节访问数据
char *v = (char *)vv;
// 将输入指针 varr 转换为 PyArrayObject 类型,以便访问 NumPy 数组的信息和方法
PyArrayObject *arr = (PyArrayObject *)varr;
// 计算数组元素的大小(以字节为单位)
npy_intp elsize = PyArray_ITEMSIZE(arr);
// 获取 NumPy 数组的比较函数指针,用于比较数组元素
PyArray_CompareFunc *cmp = PyDataType_GetArrFuncs(PyArray_DESCR(arr))->compare;
// 定义指向排序后索引数组的指针 a,进行堆排序需要偏移一位
npy_intp *a, i, j, l, tmp;
/* The array needs to be offset by one for heapsort indexing */
// 对排序索引数组 tosort 进行偏移,使其能正确用于堆排序
a = tosort - 1;
// 建立最大堆
for (l = n >> 1; l > 0; --l) {
tmp = a[l];
for (i = l, j = l << 1; j <= n;) {
// 比较子节点和父节点的值,并根据需要交换它们,以维持堆的性质
if (j < n &&
cmp(v + a[j] * elsize, v + a[j + 1] * elsize, arr) < 0) {
++j;
}
if (cmp(v + tmp * elsize, v + a[j] * elsize, arr) < 0) {
a[i] = a[j];
i = j;
j += j;
}
else {
break;
}
}
a[i] = tmp;
}
// 堆排序
for (; n > 1;) {
tmp = a[n];
a[n] = a[1];
n -= 1;
for (i = 1, j = 2; j <= n;) {
// 比较子节点和父节点的值,并根据需要交换它们,以维持堆的性质
if (j < n &&
cmp(v + a[j] * elsize, v + a[j + 1] * elsize, arr) < 0) {
++j;
}
if (cmp(v + tmp * elsize, v + a[j] * elsize, arr) < 0) {
a[i] = a[j];
i = j;
j += j;
}
else {
break;
}
}
a[i] = tmp;
}
// 堆排序完成,返回 0 表示成功
return 0;
}
/***************************************
* C > C++ dispatch
***************************************/
// 布尔类型的堆排序特化模板实例化及函数定义
template NPY_NO_EXPORT int
heapsort_<npy::bool_tag, npy_bool>(npy_bool *, npy_intp);
NPY_NO_EXPORT int
heapsort_bool(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
// 调用布尔类型的堆排序函数并返回结果
return heapsort_<npy::bool_tag>((npy_bool *)start, n);
}
// 字节类型的堆排序特化模板实例化及函数定义
template NPY_NO_EXPORT int
heapsort_<npy::byte_tag, npy_byte>(npy_byte *, npy_intp);
NPY_NO_EXPORT int
heapsort_byte(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
// 调用字节类型的堆排序函数并返回结果
return heapsort_<npy::byte_tag>((npy_byte *)start, n);
}
// 无符号字节类型的堆排序特化模板实例化及函数定义
template NPY_NO_EXPORT int
heapsort_<npy::ubyte_tag, npy_ubyte>(npy_ubyte *, npy_intp);
NPY_NO_EXPORT int
heapsort_ubyte(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
// 调用无符号字节类型的堆排序函数并返回结果
return heapsort_<npy::ubyte_tag>((npy_ubyte *)start, n);
}
// 短整型类型的堆排序特化模板实例化及函数定义
template NPY_NO_EXPORT int
heapsort_<npy::short_tag, npy_short>(npy_short *, npy_intp);
NPY_NO_EXPORT int
heapsort_short(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
// 调用短整型类型的堆排序函数并返回结果
return heapsort_<npy::short_tag>((npy_short *)start, n);
}
// 无符号短整型类型的堆排序特化模板实例化及函数定义
template NPY_NO_EXPORT int
heapsort_<npy::ushort_tag, npy_ushort>(npy_ushort *, npy_intp);
NPY_NO_EXPORT int
heapsort_ushort(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
// 调用无符号短整型类型的堆排序函数并返回结果
return heapsort_<npy::ushort_tag>((npy_ushort *)start, n);
}
// 整型类型的堆排序特化模板实例化及函数定义
template NPY_NO_EXPORT int
heapsort_<npy::int_tag, npy_int>(npy_int *, npy_intp);
NPY_NO_EXPORT int
heapsort_int(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
// 调用整型类型的堆排序函数并返回结果
return heapsort_<npy::int_tag>((npy_int *)start, n);
}
// 模板函数的声明部分截至此处
template NPY_NO_EXPORT int
heapsort_<npy::uint_tag, npy_uint>(npy_uint *, npy_intp);
NPY_NO_EXPORT int
heapsort_uint(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::uint_tag>((npy_uint *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::long_tag, npy_long>(npy_long *, npy_intp);
NPY_NO_EXPORT int
heapsort_long(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::long_tag>((npy_long *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::ulong_tag, npy_ulong>(npy_ulong *, npy_intp);
NPY_NO_EXPORT int
heapsort_ulong(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::ulong_tag>((npy_ulong *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::longlong_tag, npy_longlong>(npy_longlong *, npy_intp);
NPY_NO_EXPORT int
heapsort_longlong(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::longlong_tag>((npy_longlong *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::ulonglong_tag, npy_ulonglong>(npy_ulonglong *, npy_intp);
NPY_NO_EXPORT int
heapsort_ulonglong(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::ulonglong_tag>((npy_ulonglong *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::half_tag, npy_half>(npy_half *, npy_intp);
NPY_NO_EXPORT int
heapsort_half(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::half_tag>((npy_half *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::float_tag, npy_float>(npy_float *, npy_intp);
NPY_NO_EXPORT int
heapsort_float(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::float_tag>((npy_float *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::double_tag, npy_double>(npy_double *, npy_intp);
NPY_NO_EXPORT int
heapsort_double(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::double_tag>((npy_double *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::longdouble_tag, npy_longdouble>(npy_longdouble *, npy_intp);
NPY_NO_EXPORT int
heapsort_longdouble(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::longdouble_tag>((npy_longdouble *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::cfloat_tag, npy_cfloat>(npy_cfloat *, npy_intp);
NPY_NO_EXPORT int
heapsort_cfloat(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::cfloat_tag>((npy_cfloat *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::cdouble_tag, npy_cdouble>(npy_cdouble *, npy_intp);
NPY_NO_EXPORT int
heapsort_cdouble(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::cdouble_tag>((npy_cdouble *)start, n);
}
template NPY_NO_EXPORT int
heapsort_<npy::clongdouble_tag, npy_clongdouble>(npy_clongdouble *, npy_intp);
NPY_NO_EXPORT int
heapsort_clongdouble(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::clongdouble_tag>((npy_clongdouble *)start, n);
}
//cpp
// 声明一个模板函数,用于对日期时间类型进行堆排序,函数参数为日期时间数组和数组长度
template NPY_NO_EXPORT int heapsort_<npy::datetime_tag, npy_datetime>(npy_datetime *, npy_intp);
// 实现对日期时间类型进行堆排序的函数,函数参数为起始地址、数组长度和一个未使用的参数(varr)
NPY_NO_EXPORT int heapsort_datetime(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
// 调用模板函数 heapsort_<npy::datetime_tag> 进行堆排序,返回排序后的结果
return heapsort_<npy::datetime_tag>((npy_datetime *)start, n);
}
// 类似地,声明一个模板函数,用于对时间差类型进行堆排序
template NPY_NO_EXPORT int heapsort_<npy::timedelta_tag, npy_timedelta>(npy_timedelta *, npy_intp);
// 实现对时间差类型进行堆排序的函数,参数和用法与上一个函数相似
NPY_NO_EXPORT int heapsort_timedelta(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
return heapsort_<npy::timedelta_tag>((npy_timedelta *)start, n);
}
// 声明一个模板函数,用于对布尔类型进行堆排序
template NPY_NO_EXPORT int aheapsort_<npy::bool_tag, npy_bool>(npy_bool *vv, npy_intp *tosort, npy_intp n);
// 实现对布尔类型进行堆排序的函数,参数和用法与上一个函数相似
NPY_NO_EXPORT int aheapsort_bool(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::bool_tag>((npy_bool *)vv, tosort, n);
}
// 类似地,声明模板函数和实现函数,用于对字节类型进行堆排序
template NPY_NO_EXPORT int aheapsort_<npy::byte_tag, npy_byte>(npy_byte *vv, npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int aheapsort_byte(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::byte_tag>((npy_byte *)vv, tosort, n);
}
// 对无符号字节类型进行堆排序
template NPY_NO_EXPORT int aheapsort_<npy::ubyte_tag, npy_ubyte>(npy_ubyte *vv, npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int aheapsort_ubyte(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::ubyte_tag>((npy_ubyte *)vv, tosort, n);
}
// 对短整型进行堆排序
template NPY_NO_EXPORT int aheapsort_<npy::short_tag, npy_short>(npy_short *vv, npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int aheapsort_short(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::short_tag>((npy_short *)vv, tosort, n);
}
// 对无符号短整型进行堆排序
template NPY_NO_EXPORT int aheapsort_<npy::ushort_tag, npy_ushort>(npy_ushort *vv, npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int aheapsort_ushort(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::ushort_tag>((npy_ushort *)vv, tosort, n);
}
// 对整型进行堆排序
template NPY_NO_EXPORT int aheapsort_<npy::int_tag, npy_int>(npy_int *vv, npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int aheapsort_int(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::int_tag>((npy_int *)vv, tosort, n);
}
// 对无符号整型进行堆排序
template NPY_NO_EXPORT int aheapsort_<npy::uint_tag, npy_uint>(npy_uint *vv, npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int aheapsort_uint(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::uint_tag>((npy_uint *)vv, tosort, n);
}
// 对长整型进行堆排序
template NPY_NO_EXPORT int aheapsort_<npy::long_tag, npy_long>(npy_long *vv, npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int aheapsort_long(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::long_tag>((npy_long *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::ulong_tag, npy_ulong>(npy_ulong *vv, npy_intp *tosort,
npy_intp n);
NPY_NO_EXPORT int
aheapsort_ulong(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::ulong_tag>((npy_ulong *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::longlong_tag, npy_longlong>(npy_longlong *vv, npy_intp *tosort,
npy_intp n);
NPY_NO_EXPORT int
aheapsort_longlong(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
return aheapsort_<npy::longlong_tag>((npy_longlong *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::ulonglong_tag, npy_ulonglong>(npy_ulonglong *vv,
npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int
aheapsort_ulonglong(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
return aheapsort_<npy::ulonglong_tag>((npy_ulonglong *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::half_tag, npy_half>(npy_half *vv, npy_intp *tosort,
npy_intp n);
NPY_NO_EXPORT int
aheapsort_half(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::half_tag>((npy_half *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::float_tag, npy_float>(npy_float *vv, npy_intp *tosort,
npy_intp n);
NPY_NO_EXPORT int
aheapsort_float(void *vv, npy_intp *tosort, npy_intp n, void *NPY_UNUSED(varr))
{
return aheapsort_<npy::float_tag>((npy_float *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::double_tag, npy_double>(npy_double *vv, npy_intp *tosort,
npy_intp n);
NPY_NO_EXPORT int
aheapsort_double(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
return aheapsort_<npy::double_tag>((npy_double *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::longdouble_tag, npy_longdouble>(npy_longdouble *vv,
npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int
aheapsort_longdouble(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
return aheapsort_<npy::longdouble_tag>((npy_longdouble *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::cfloat_tag, npy_cfloat>(npy_cfloat *vv, npy_intp *tosort,
npy_intp n);
NPY_NO_EXPORT int
aheapsort_cfloat(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
return aheapsort_<npy::cfloat_tag>((npy_cfloat *)vv, tosort, n);
}
template NPY_NO_EXPORT int
aheapsort_<npy::cdouble_tag, npy_cdouble>(npy_cdouble *vv, npy_intp *tosort,
npy_intp n);
// 调用模板函数 aheapsort_,对 void 类型的数组进行堆排序,并返回排序后的结果
aheapsort_cdouble(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
// 调用特化的 aheapsort_ 函数,处理 npy_cdouble 类型的数据
return aheapsort_<npy::cdouble_tag>((npy_cdouble *)vv, tosort, n);
}
// 显式实例化模板函数 aheapsort_,处理 npy_clongdouble 类型的数组排序
template NPY_NO_EXPORT int
aheapsort_<npy::clongdouble_tag, npy_clongdouble>(npy_clongdouble *vv,
npy_intp *tosort,
npy_intp n);
NPY_NO_EXPORT int
// 对 void 类型的数组进行堆排序,返回排序后的结果
aheapsort_clongdouble(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
// 调用特化的 aheapsort_ 函数,处理 npy_clongdouble 类型的数据
return aheapsort_<npy::clongdouble_tag>((npy_clongdouble *)vv, tosort, n);
}
// 显式实例化模板函数 aheapsort_,处理 npy_datetime 类型的数组排序
template NPY_NO_EXPORT int
aheapsort_<npy::datetime_tag, npy_datetime>(npy_datetime *vv, npy_intp *tosort,
npy_intp n);
NPY_NO_EXPORT int
// 对 void 类型的数组进行堆排序,返回排序后的结果
aheapsort_datetime(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
// 调用特化的 aheapsort_ 函数,处理 npy_datetime 类型的数据
return aheapsort_<npy::datetime_tag>((npy_datetime *)vv, tosort, n);
}
// 显式实例化模板函数 aheapsort_,处理 npy_timedelta 类型的数组排序
template NPY_NO_EXPORT int
aheapsort_<npy::timedelta_tag, npy_timedelta>(npy_timedelta *vv,
npy_intp *tosort, npy_intp n);
NPY_NO_EXPORT int
// 对 void 类型的数组进行堆排序,返回排序后的结果
aheapsort_timedelta(void *vv, npy_intp *tosort, npy_intp n,
void *NPY_UNUSED(varr))
{
// 调用特化的 aheapsort_ 函数,处理 npy_timedelta 类型的数据
return aheapsort_<npy::timedelta_tag>((npy_timedelta *)vv, tosort, n);
}
// 对 void 类型的数组进行堆排序,使用 string_heapsort_ 函数处理 npy::string_tag 类型的数据
NPY_NO_EXPORT int
heapsort_string(void *start, npy_intp n, void *varr)
{
return string_heapsort_<npy::string_tag>((npy_char *)start, n, varr);
}
// 对 void 类型的数组进行堆排序,使用 string_heapsort_ 函数处理 npy::unicode_tag 类型的数据
NPY_NO_EXPORT int
heapsort_unicode(void *start, npy_intp n, void *varr)
{
return string_heapsort_<npy::unicode_tag>((npy_ucs4 *)start, n, varr);
}
// 对 void 类型的数组进行堆排序,使用 string_aheapsort_ 函数处理 npy::string_tag 类型的数据
NPY_NO_EXPORT int
aheapsort_string(void *vv, npy_intp *tosort, npy_intp n, void *varr)
{
return string_aheapsort_<npy::string_tag>((npy_char *)vv, tosort, n, varr);
}
// 对 void 类型的数组进行堆排序,使用 string_aheapsort_ 函数处理 npy::unicode_tag 类型的数据
NPY_NO_EXPORT int
aheapsort_unicode(void *vv, npy_intp *tosort, npy_intp n, void *varr)
{
return string_aheapsort_<npy::unicode_tag>((npy_ucs4 *)vv, tosort, n,
varr);
}
.\numpy\numpy\_core\src\npysort\heapsort.hpp
namespace np::sort {
template <typename T>
inline bool LessThan(const T &a, const T &b)
{
// 比较函数,根据类型 T 的不同,进行不同的比较操作
if constexpr (std::is_floating_point_v<T>) {
// 如果 T 是浮点数类型,则考虑 NaN 的情况进行比较
return a < b || (b != b && a == a);
}
else if constexpr(std::is_same_v<T, Half>) {
// 如果 T 是 Half 类型,则调用其自定义的 Less 方法进行比较
bool a_nn = !a.IsNaN();
return b.IsNaN() ? a_nn : a_nn && a.Less(b);
}
else {
// 对于其他类型 T,直接进行常规的小于比较
return a < b;
}
}
// NUMERIC SORTS
template <typename T>
inline void Heap(T *start, SSize n)
{
SSize i, j, l;
// 堆排序需要将数组的索引偏移一位,以符合堆的索引规则
T tmp, *a = start - 1;
// 自上而下建堆过程
for (l = n >> 1; l > 0; --l) {
tmp = a[l];
for (i = l, j = l << 1; j <= n;) {
if (j < n && LessThan(a[j], a[j + 1])) {
j += 1;
}
if (LessThan(tmp, a[j])) {
a[i] = a[j];
i = j;
j += j;
}
else {
break;
}
}
a[i] = tmp;
}
// 堆排序的下沉调整过程
for (; n > 1;) {
tmp = a[n];
a[n] = a[1];
n -= 1;
for (i = 1, j = 2; j <= n;) {
if (j < n && LessThan(a[j], a[j + 1])) {
j++;
}
if (LessThan(tmp, a[j])) {
a[i] = a[j];
i = j;
j += j;
}
else {
break;
}
}
a[i] = tmp;
}
}
} // namespace np::sort
.\numpy\numpy\_core\src\npysort\highway_qsort.dispatch.cpp
// 定义宏,指定只使用静态版本的 VQSort
// 定义模板函数 DISPATCH_VQSORT,用于生成特定类型的 QSort 函数
template<> void NPY_CPU_DISPATCH_CURFX(QSort)(TYPE *arr, intptr_t size) \
{ \
// 调用 HWY 命名空间中的 VQSortStatic 函数,对数组进行升序排序
hwy::HWY_NAMESPACE::VQSortStatic(arr, size, hwy::SortAscending()); \
} \
namespace np { namespace highway { namespace qsort_simd {
// 实例化 DISPATCH_VQSORT 模板函数,针对不同的数据类型
DISPATCH_VQSORT(int32_t)
DISPATCH_VQSORT(uint32_t)
DISPATCH_VQSORT(int64_t)
DISPATCH_VQSORT(uint64_t)
DISPATCH_VQSORT(double)
DISPATCH_VQSORT(float)
} } } // np::highway::qsort_simd
.\numpy\numpy\_core\src\npysort\highway_qsort.hpp
// 如果未定义 NUMPY_SRC_COMMON_NPYSORT_HWY_SIMD_QSORT_HPP 宏,则包含该头文件,以防止多次包含
// 包含 common.hpp 头文件,引入通用的常量、函数和数据结构定义
namespace np { namespace highway { namespace qsort_simd {
// 命名空间声明:np -> highway -> qsort_simd,用于组织 qsort_simd 相关的函数和数据结构
// 如果未定义 NPY_DISABLE_OPTIMIZATION 宏,则包含 highway_qsort.dispatch.h 头文件,该文件可能包含了针对 QSort 和 QSelect 函数的优化版本的声明和定义
NPY_CPU_DISPATCH_DECLARE(template <typename T> void QSort, (T *arr, npy_intp size))
NPY_CPU_DISPATCH_DECLARE(template <typename T> void QSelect, (T* arr, npy_intp num, npy_intp kth))
// 使用 NPY_CPU_DISPATCH_DECLARE 宏声明 QSort 和 QSelect 函数模板,这些函数用于对数组进行排序和选择操作
// 如果未定义 NPY_DISABLE_OPTIMIZATION 宏,则包含 highway_qsort_16bit.dispatch.h 头文件,该文件可能包含了针对 QSort 和 QSelect 函数在 16 位数据类型上的优化版本的声明和定义
NPY_CPU_DISPATCH_DECLARE(template <typename T> void QSort, (T *arr, npy_intp size))
NPY_CPU_DISPATCH_DECLARE(template <typename T> void QSelect, (T* arr, npy_intp num, npy_intp kth))
// 使用 NPY_CPU_DISPATCH_DECLARE 宏声明另一组 QSort 和 QSelect 函数模板,针对 16 位数据类型
} } } // np::highway::qsort_simd
// 命名空间封闭:结束 np -> highway -> qsort_simd 命名空间的声明
// 结束 NUMPY_SRC_COMMON_NPYSORT_HWY_SIMD_QSORT_HPP 头文件的条件编译指令
.\numpy\numpy\_core\src\npysort\highway_qsort_16bit.dispatch.cpp
// 包含自定义的排序头文件 "highway_qsort.hpp"
// 定义宏 VQSORT_ONLY_STATIC 为 1,用于特定的静态排序
// 包含高速公路库中的排序实现头文件 "hwy/contrib/sort/vqsort-inl.h"
// 包含自定义的快速排序头文件 "quicksort.hpp"
// 命名空间声明:np -> highway -> qsort_simd
namespace np { namespace highway { namespace qsort_simd {
// 模板特化:处理半精度浮点数数组的 QSort 函数
template<> void NPY_CPU_DISPATCH_CURFX(QSort)(Half *arr, intptr_t size)
{
// 如果支持半精度浮点数操作
// 调用高速公路库中的静态向量化排序,对半精度浮点数数组 arr 进行升序排序
hwy::HWY_NAMESPACE::VQSortStatic(reinterpret_cast<hwy::float16_t*>(arr), size, hwy::SortAscending());
// 如果不支持半精度浮点数操作
// 调用自定义的快速排序,对半精度浮点数数组 arr 进行排序
sort::Quick(arr, size);
}
// 模板特化:处理无符号 16 位整数数组的 QSort 函数
template<> void NPY_CPU_DISPATCH_CURFX(QSort)(uint16_t *arr, intptr_t size)
{
// 调用高速公路库中的静态向量化排序,对无符号 16 位整数数组 arr 进行升序排序
hwy::HWY_NAMESPACE::VQSortStatic(arr, size, hwy::SortAscending());
}
// 模板特化:处理有符号 16 位整数数组的 QSort 函数
template<> void NPY_CPU_DISPATCH_CURFX(QSort)(int16_t *arr, intptr_t size)
{
// 调用高速公路库中的静态向量化排序,对有符号 16 位整数数组 arr 进行升序排序
hwy::HWY_NAMESPACE::VQSortStatic(arr, size, hwy::SortAscending());
}
} } } // np::highway::qsort_simd
.\numpy\numpy\_core\src\npysort\mergesort.cpp
/*
* The purpose of this module is to add faster sort functions
* that are type-specific. This is done by altering the
* function table for the builtin descriptors.
*
* These sorting functions are copied almost directly from numarray
* with a few modifications (complex comparisons compare the imaginary
* part if the real parts are equal, for example), and the names
* are changed.
*
* The original sorting code is due to Charles R. Harris who wrote
* it for numarray.
*/
/*
* Quick sort is usually the fastest, but the worst case scenario can
* be slower than the merge and heap sorts. The merge sort requires
* extra memory and so for large arrays may not be useful.
*
* The merge sort is *stable*, meaning that equal components
* are unmoved from their entry versions, so it can be used to
* implement lexicographic sorting on multiple keys.
*
* The heap sort is included for completeness.
*/
/*
*****************************************************************************
** NUMERIC SORTS **
*****************************************************************************
*/
template <typename Tag, typename type>
static void
mergesort0_(type *pl, type *pr, type *pw)
{
type vp, *pi, *pj, *pk, *pm;
if (pr - pl > SMALL_MERGESORT) {
/* merge sort */
pm = pl + ((pr - pl) >> 1); // Calculate middle point
mergesort0_<Tag>(pl, pm, pw); // Recursively sort left half
mergesort0_<Tag>(pm, pr, pw); // Recursively sort right half
for (pi = pw, pj = pl; pj < pm;) { // Copy elements to workspace
*pi++ = *pj++;
}
pi = pw + (pm - pl); // Set insertion point in workspace
pj = pw;
pk = pl;
while (pj < pi && pm < pr) { // Merge sorted halves
if (Tag::less(*pm, *pj)) { // Compare using custom tag less function
*pk++ = *pm++;
}
else {
*pk++ = *pj++;
}
}
while (pj < pi) { // Copy remaining elements from workspace
*pk++ = *pj++;
}
}
else {
/* insertion sort */
for (pi = pl + 1; pi < pr; ++pi) { // Iterate through elements
vp = *pi;
pj = pi;
pk = pi - 1;
while (pj > pl && Tag::less(vp, *pk)) { // Perform insertion sort
*pj-- = *pk--;
}
*pj = vp;
}
}
}
template <typename Tag, typename type>
NPY_NO_EXPORT int
mergesort_(type *start, npy_intp num)
{
type *pl, *pr, *pw;
pl = start;
pr = pl + num;
pw = (type *)malloc((num / 2) * sizeof(type)); // Allocate workspace
if (pw == NULL) {
return -NPY_ENOMEM; // Return error if allocation fails
}
mergesort0_<Tag>(pl, pr, pw); // Call mergesort function
free(pw); // Free allocated workspace
return 0; // Return success
}
template <typename Tag, typename type>
static void
amergesort0_(npy_intp *pl, npy_intp *pr, type *v, npy_intp *pw)
{
type vp;
npy_intp vi, *pi, *pj, *pk, *pm;
if (pr - pl > SMALL_MERGESORT) {
/* 如果待排序的子数组长度大于预设的小数组长度,使用归并排序 */
// 计算中间位置
pm = pl + ((pr - pl) >> 1);
// 对左半部分进行归并排序
amergesort0_<Tag>(pl, pm, v, pw);
// 对右半部分进行归并排序
amergesort0_<Tag>(pm, pr, v, pw);
// 合并两个有序子数组
for (pi = pw, pj = pl; pj < pm;) {
*pi++ = *pj++;
}
// 设置合并后的起始位置
pi = pw + (pm - pl);
pj = pw;
pk = pl;
// 合并左右两个子数组
while (pj < pi && pm < pr) {
if (Tag::less(v[*pm], v[*pj])) {
*pk++ = *pm++;
}
else {
*pk++ = *pj++;
}
}
// 处理剩余的元素
while (pj < pi) {
*pk++ = *pj++;
}
}
else {
/* 如果待排序的子数组长度不大于预设的小数组长度,使用插入排序 */
// 插入排序
for (pi = pl + 1; pi < pr; ++pi) {
vi = *pi;
vp = v[vi];
pj = pi;
pk = pi - 1;
// 寻找合适的插入位置
while (pj > pl && Tag::less(vp, v[*pk])) {
*pj-- = *pk--;
}
// 插入元素
*pj = vi;
}
}
}
template <typename Tag, typename type>
NPY_NO_EXPORT int
amergesort_(type *v, npy_intp *tosort, npy_intp num)
{
npy_intp *pl, *pr, *pw;
pl = tosort; // 初始化指向排序数组的起始位置
pr = pl + num; // 初始化指向排序数组的结束位置的下一个位置
pw = (npy_intp *)malloc((num / 2) * sizeof(npy_intp)); // 分配临时空间,用于归并排序中的工作数组
if (pw == NULL) { // 如果分配失败,则返回内存不足错误码
return -NPY_ENOMEM;
}
amergesort0_<Tag>(pl, pr, v, pw); // 调用归并排序的实现函数
free(pw); // 释放临时工作数组的内存空间
return 0; // 返回排序成功
}
/*
*****************************************************************************
** STRING SORTS **
*****************************************************************************
*/
template <typename Tag, typename type>
static void
mergesort0_(type *pl, type *pr, type *pw, type *vp, size_t len)
{
type *pi, *pj, *pk, *pm;
if ((size_t)(pr - pl) > SMALL_MERGESORT * len) { // 如果数组长度大于指定值,执行归并排序
/* merge sort */
pm = pl + (((pr - pl) / len) >> 1) * len; // 计算中间位置并取整作为分割点
mergesort0_<Tag>(pl, pm, pw, vp, len); // 递归调用归并排序左半部分
mergesort0_<Tag>(pm, pr, pw, vp, len); // 递归调用归并排序右半部分
Tag::copy(pw, pl, pm - pl); // 复制左半部分到临时工作数组
pi = pw + (pm - pl);
pj = pw;
pk = pl;
while (pj < pi && pm < pr) { // 归并两个有序数组
if (Tag::less(pm, pj, len)) { // 如果右半部分元素小于左半部分元素
Tag::copy(pk, pm, len); // 将右半部分元素复制到原数组
pm += len;
pk += len;
}
else {
Tag::copy(pk, pj, len); // 将左半部分元素复制到原数组
pj += len;
pk += len;
}
}
Tag::copy(pk, pj, pi - pj); // 处理剩余的元素
}
else {
/* insertion sort */ // 如果数组长度小于等于指定值,执行插入排序
for (pi = pl + len; pi < pr; pi += len) { // 从第二个元素开始,依次将元素插入已排序的序列中
Tag::copy(vp, pi, len); // 备份当前元素
pj = pi;
pk = pi - len;
while (pj > pl && Tag::less(vp, pk, len)) { // 向前比较并移动元素,保持序列有序
Tag::copy(pj, pk, len);
pj -= len;
pk -= len;
}
Tag::copy(pj, vp, len); // 将备份的元素插入到正确的位置
}
}
}
template <typename Tag, typename type>
static int
string_mergesort_(type *start, npy_intp num, void *varr)
{
PyArrayObject *arr = (PyArrayObject *)varr;
size_t elsize = PyArray_ITEMSIZE(arr);
size_t len = elsize / sizeof(type);
type *pl, *pr, *pw, *vp;
int err = 0;
/* Items that have zero size don't make sense to sort */
if (elsize == 0) { // 如果元素大小为0,直接返回排序成功
return 0;
}
pl = start; // 初始化指向排序数组的起始位置
pr = pl + num * len; // 初始化指向排序数组的结束位置的下一个位置
pw = (type *)malloc((num / 2) * elsize); // 分配临时空间,用于归并排序中的工作数组
if (pw == NULL) { // 如果分配失败,则返回内存不足错误码
err = -NPY_ENOMEM;
goto fail_0;
}
vp = (type *)malloc(elsize); // 分配临时空间,用于备份当前元素
if (vp == NULL) { // 如果分配失败,则返回内存不足错误码
err = -NPY_ENOMEM;
goto fail_1;
}
mergesort0_<Tag>(pl, pr, pw, vp, len); // 调用归并排序的实现函数
free(vp); // 释放临时备份元素的内存空间
fail_1:
free(pw); // 释放临时工作数组的内存空间
fail_0:
return err; // 返回排序的结果
}
template <typename Tag, typename type>
static void
amergesort0_(npy_intp *pl, npy_intp *pr, type *v, npy_intp *pw, size_t len)
{
type *vp;
npy_intp vi, *pi, *pj, *pk, *pm;
if (pr - pl > SMALL_MERGESORT) {
/* 如果子数组长度大于SMALL_MERGESORT,执行归并排序 */
// 计算中间点
pm = pl + ((pr - pl) >> 1);
// 递归调用归并排序,对左半部分进行排序
amergesort0_<Tag>(pl, pm, v, pw, len);
// 递归调用归并排序,对右半部分进行排序
amergesort0_<Tag>(pm, pr, v, pw, len);
// 将左右两部分合并到临时数组pw中
for (pi = pw, pj = pl; pj < pm;) {
*pi++ = *pj++;
}
// 初始化指针pi指向合并后的起始位置
pi = pw + (pm - pl);
pj = pw;
pk = pl;
// 归并左右两部分,根据排序规则决定元素存放位置
while (pj < pi && pm < pr) {
if (Tag::less(v + (*pm) * len, v + (*pj) * len, len)) {
*pk++ = *pm++;
}
else {
*pk++ = *pj++;
}
}
// 处理剩余元素
while (pj < pi) {
*pk++ = *pj++;
}
}
else {
/* 如果子数组长度不大于SMALL_MERGESORT,执行插入排序 */
// 使用插入排序对当前子数组进行排序
for (pi = pl + 1; pi < pr; ++pi) {
vi = *pi; // 当前待插入的值
vp = v + vi * len; // 对应的数据起始地址
pj = pi; // 待比较位置
pk = pi - 1; // 前一个位置
// 向前比较并移动元素,直到找到插入位置
while (pj > pl && Tag::less(vp, v + (*pk) * len, len)) {
*pj-- = *pk--;
}
// 插入当前值到正确位置
*pj = vi;
}
}
这段代码实现了一个通用的归并排序算法,其中根据子数组长度选择使用归并排序或插入排序。
}
template <typename Tag, typename type>
static int
string_amergesort_(type *v, npy_intp *tosort, npy_intp num, void *varr)
{
PyArrayObject *arr = (PyArrayObject *)varr; // 将void指针转换为PyArrayObject类型
size_t elsize = PyArray_ITEMSIZE(arr); // 计算数组元素的大小
size_t len = elsize / sizeof(type); // 计算数组中元素的个数
npy_intp *pl, *pr, *pw; // 定义指向npy_intp类型的指针
/* Items that have zero size don't make sense to sort */
if (elsize == 0) {
return 0; // 如果元素大小为0,返回0
}
pl = tosort; // 初始化排序起始位置为tosort
pr = pl + num; // 初始化排序结束位置为tosort + num
pw = (npy_intp *)malloc((num / 2) * sizeof(npy_intp)); // 分配内存给pw
if (pw == NULL) {
return -NPY_ENOMEM; // 如果内存分配失败,返回内存不足错误码
}
amergesort0_<Tag>(pl, pr, v, pw, len); // 调用模板函数进行排序
free(pw); // 释放pw指向的内存空间
return 0; // 返回0表示成功
}
/*
*****************************************************************************
** GENERIC SORT **
*****************************************************************************
*/
static void
npy_mergesort0(char *pl, char *pr, char *pw, char *vp, npy_intp elsize,
PyArray_CompareFunc *cmp, PyArrayObject *arr)
{
char *pi, *pj, *pk, *pm; // 定义指向字符的指针
if (pr - pl > SMALL_MERGESORT * elsize) {
/* merge sort */
pm = pl + (((pr - pl) / elsize) >> 1) * elsize; // 计算中间点pm
npy_mergesort0(pl, pm, pw, vp, elsize, cmp, arr); // 递归调用归并排序左半部分
npy_mergesort0(pm, pr, pw, vp, elsize, cmp, arr); // 递归调用归并排序右半部分
GENERIC_COPY(pw, pl, pm - pl); // 复制左半部分到pw中
pi = pw + (pm - pl); // 初始化pi为pw + (pm - pl)
pj = pw; // 初始化pj为pw
pk = pl; // 初始化pk为pl
while (pj < pi && pm < pr) {
if (cmp(pm, pj, arr) < 0) { // 如果pm < pj,则复制pm到pk位置
GENERIC_COPY(pk, pm, elsize);
pm += elsize;
pk += elsize;
}
else { // 否则,复制pj到pk位置
GENERIC_COPY(pk, pj, elsize);
pj += elsize;
pk += elsize;
}
}
GENERIC_COPY(pk, pj, pi - pj); // 复制剩余的元素到pk位置
}
else {
/* insertion sort */
for (pi = pl + elsize; pi < pr; pi += elsize) { // 插入排序
GENERIC_COPY(vp, pi, elsize);
pj = pi;
pk = pi - elsize;
while (pj > pl && cmp(vp, pk, arr) < 0) {
GENERIC_COPY(pj, pk, elsize);
pj -= elsize;
pk -= elsize;
}
GENERIC_COPY(pj, vp, elsize);
}
}
}
NPY_NO_EXPORT int
npy_mergesort(void *start, npy_intp num, void *varr)
{
PyArrayObject *arr = (PyArrayObject *)varr; // 将void指针转换为PyArrayObject类型
npy_intp elsize = PyArray_ITEMSIZE(arr); // 计算数组元素的大小
PyArray_CompareFunc *cmp = PyDataType_GetArrFuncs(PyArray_DESCR(arr))->compare; // 获取比较函数
char *pl = (char *)start; // 初始化数组起始位置
char *pr = pl + num * elsize; // 初始化数组结束位置
char *pw;
char *vp;
int err = -NPY_ENOMEM;
/* Items that have zero size don't make sense to sort */
if (elsize == 0) {
return 0; // 如果元素大小为0,返回0
}
pw = (char *)malloc((num >> 1) * elsize); // 分配内存给pw
vp = (char *)malloc(elsize); // 分配内存给vp
if (pw != NULL && vp != NULL) {
npy_mergesort0(pl, pr, pw, vp, elsize, cmp, arr); // 调用归并排序函数
err = 0; // 表示排序成功
}
free(vp); // 释放vp指向的内存空间
free(pw); // 释放pw指向的内存空间
return err; // 返回错误码或者0表示成功
}
static void
/*
* 使用合并排序算法对数组进行排序,其中包含以下参数:
* - pl: 数组的左边界指针
* - pr: 数组的右边界指针
* - v: 指向数据的指针
* - pw: 临时数组的指针,用于存储排序过程中的中间结果
* - elsize: 元素大小
* - cmp: 比较函数指针,用于比较数组元素
* - arr: NumPy 数组对象指针,包含排序数据的描述信息
*/
npy_amergesort0(npy_intp *pl, npy_intp *pr, char *v, npy_intp *pw,
npy_intp elsize, PyArray_CompareFunc *cmp, PyArrayObject *arr)
{
char *vp;
npy_intp vi, *pi, *pj, *pk, *pm;
if (pr - pl > SMALL_MERGESORT) {
/* 使用合并排序 */
pm = pl + ((pr - pl) >> 1);
npy_amergesort0(pl, pm, v, pw, elsize, cmp, arr);
npy_amergesort0(pm, pr, v, pw, elsize, cmp, arr);
for (pi = pw, pj = pl; pj < pm;) {
*pi++ = *pj++;
}
pi = pw + (pm - pl);
pj = pw;
pk = pl;
while (pj < pi && pm < pr) {
if (cmp(v + (*pm) * elsize, v + (*pj) * elsize, arr) < 0) {
*pk++ = *pm++;
}
else {
*pk++ = *pj++;
}
}
while (pj < pi) {
*pk++ = *pj++;
}
}
else {
/* 使用插入排序 */
for (pi = pl + 1; pi < pr; ++pi) {
vi = *pi;
vp = v + vi * elsize;
pj = pi;
pk = pi - 1;
while (pj > pl && cmp(vp, v + (*pk) * elsize, arr) < 0) {
*pj-- = *pk--;
}
*pj = vi;
}
}
}
/*
* 使用合并排序算法对数组进行排序的入口函数。
* 其中包含以下参数:
* - v: 指向数组的指针
* - tosort: 指向要排序的元素的指针数组
* - num: 要排序的元素数量
* - varr: 指向 NumPy 数组对象的指针
* 返回值:
* - 0:排序成功
* - -NPY_ENOMEM:内存分配失败
*/
NPY_NO_EXPORT int
npy_amergesort(void *v, npy_intp *tosort, npy_intp num, void *varr)
{
PyArrayObject *arr = (PyArrayObject *)varr;
npy_intp elsize = PyArray_ITEMSIZE(arr);
PyArray_CompareFunc *cmp = PyDataType_GetArrFuncs(PyArray_DESCR(arr))->compare;
npy_intp *pl, *pr, *pw;
/* 如果元素大小为0,则没有意义进行排序 */
if (elsize == 0) {
return 0;
}
pl = tosort;
pr = pl + num;
pw = (npy_intp *)malloc((num >> 1) * sizeof(npy_intp));
if (pw == NULL) {
return -NPY_ENOMEM;
}
npy_amergesort0(pl, pr, (char *)v, pw, elsize, cmp, arr);
free(pw);
return 0;
}
/***************************************
* C > C++ 调度
***************************************/
/*
* 对布尔类型数组使用合并排序的函数
* - start: 指向布尔类型数组的指针
* - num: 要排序的元素数量
* - varr: 指向 NumPy 数组对象的指针(未使用)
*/
NPY_NO_EXPORT int
mergesort_bool(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
return mergesort_<npy::bool_tag>((npy_bool *)start, num);
}
/*
* 对字节类型数组使用合并排序的函数
* - start: 指向字节类型数组的指针
* - num: 要排序的元素数量
* - varr: 指向 NumPy 数组对象的指针(未使用)
*/
NPY_NO_EXPORT int
mergesort_byte(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
return mergesort_<npy::byte_tag>((npy_byte *)start, num);
}
/*
* 对无符号字节类型数组使用合并排序的函数
* - start: 指向无符号字节类型数组的指针
* - num: 要排序的元素数量
* - varr: 指向 NumPy 数组对象的指针(未使用)
*/
NPY_NO_EXPORT int
mergesort_ubyte(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
return mergesort_<npy::ubyte_tag>((npy_ubyte *)start, num);
}
/*
* 对短整型数组使用合并排序的函数
* - start: 指向短整型数组的指针
* - num: 要排序的元素数量
* - varr: 指向 NumPy 数组对象的指针(未使用)
*/
NPY_NO_EXPORT int
mergesort_short(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
return mergesort_<npy::short_tag>((npy_short *)start, num);
}
/*
* 对无符号短整型数组使用合并排序的函数
* - start: 指向无符号短整型数组的指针
* - num: 要排序的元素数量
* - varr: 指向 NumPy 数组对象的指针(未使用)
*/
NPY_NO_EXPORT int
mergesort_ushort(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
return mergesort_<npy::ushort_tag>((npy_ushort *)start, num);
}
/*
* 对整型数组使用合并排序的函数
* - start: 指向整型数组的指针
* - num: 要排序的元素数量
* - varr: 指向 NumPy 数组对象的指针(未使用)
*/
NPY_NO_EXPORT int
mergesort_int(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
return mergesort_<npy::int_tag>((npy_int *)start, num);
}
/*
* 对无符号整型数组使用合并排序的函数
* - start: 指向无符号整型数组的指针
* - num: 要排序的元素数量
* - varr: 指向 NumPy 数组对象的指针(未使用)
*/
NPY_NO_EXPORT int
mergesort_uint(void *start, npy_intp num, void *NPY_UNUSED(varr)))
{
// mergesort_uint 函数尚未完成
return 0;
}
// 调用 mergesort_<npy::uint_tag> 函数,对给定的数组进行归并排序,并返回排序后的结果。
return mergesort_<npy::uint_tag>((npy_uint *)start, num);
NPY_NO_EXPORT int
mergesort_long(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::long_tag 进行排序,返回排序结果
return mergesort_<npy::long_tag>((npy_long *)start, num);
}
NPY_NO_EXPORT int
mergesort_ulong(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::ulong_tag 进行排序,返回排序结果
return mergesort_<npy::ulong_tag>((npy_ulong *)start, num);
}
NPY_NO_EXPORT int
mergesort_longlong(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::longlong_tag 进行排序,返回排序结果
return mergesort_<npy::longlong_tag>((npy_longlong *)start, num);
}
NPY_NO_EXPORT int
mergesort_ulonglong(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::ulonglong_tag 进行排序,返回排序结果
return mergesort_<npy::ulonglong_tag>((npy_ulonglong *)start, num);
}
NPY_NO_EXPORT int
mergesort_half(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::half_tag 进行排序,返回排序结果
return mergesort_<npy::half_tag>((npy_half *)start, num);
}
NPY_NO_EXPORT int
mergesort_float(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::float_tag 进行排序,返回排序结果
return mergesort_<npy::float_tag>((npy_float *)start, num);
}
NPY_NO_EXPORT int
mergesort_double(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::double_tag 进行排序,返回排序结果
return mergesort_<npy::double_tag>((npy_double *)start, num);
}
NPY_NO_EXPORT int
mergesort_longdouble(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::longdouble_tag 进行排序,返回排序结果
return mergesort_<npy::longdouble_tag>((npy_longdouble *)start, num);
}
NPY_NO_EXPORT int
mergesort_cfloat(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::cfloat_tag 进行排序,返回排序结果
return mergesort_<npy::cfloat_tag>((npy_cfloat *)start, num);
}
NPY_NO_EXPORT int
mergesort_cdouble(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::cdouble_tag 进行排序,返回排序结果
return mergesort_<npy::cdouble_tag>((npy_cdouble *)start, num);
}
NPY_NO_EXPORT int
mergesort_clongdouble(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::clongdouble_tag 进行排序,返回排序结果
return mergesort_<npy::clongdouble_tag>((npy_clongdouble *)start, num);
}
NPY_NO_EXPORT int
mergesort_datetime(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::datetime_tag 进行排序,返回排序结果
return mergesort_<npy::datetime_tag>((npy_datetime *)start, num);
}
NPY_NO_EXPORT int
mergesort_timedelta(void *start, npy_intp num, void *NPY_UNUSED(varr))
{
// 调用模板函数 mergesort_,使用 npy::timedelta_tag 进行排序,返回排序结果
return mergesort_<npy::timedelta_tag>((npy_timedelta *)start, num);
}
NPY_NO_EXPORT int
amergesort_bool(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,使用 npy::bool_tag 进行排序,返回排序结果
return amergesort_<npy::bool_tag>((npy_bool *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_byte(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,使用 npy::byte_tag 进行排序,返回排序结果
return amergesort_<npy::byte_tag>((npy_byte *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_ubyte(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,使用 npy::ubyte_tag 进行排序,返回排序结果
return amergesort_<npy::ubyte_tag>((npy_ubyte *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_short(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,使用 npy::short_tag 进行排序,返回排序结果
return amergesort_<npy::short_tag>((npy_short *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_ushort(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,使用 npy::ushort_tag 进行排序,返回排序结果
return amergesort_<npy::ushort_tag>((npy_ushort *)start, tosort, num);
}
以上是给定代码的注释。
# 调用amergesort_<npy::ushort_tag>函数,传入参数并返回结果
return amergesort_<npy::ushort_tag>((npy_ushort *)start, tosort, num);
NPY_NO_EXPORT int
amergesort_int(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理整数类型的排序,返回排序完成的状态
return amergesort_<npy::int_tag>((npy_int *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_uint(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理无符号整数类型的排序,返回排序完成的状态
return amergesort_<npy::uint_tag>((npy_uint *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_long(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理长整型的排序,返回排序完成的状态
return amergesort_<npy::long_tag>((npy_long *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_ulong(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理无符号长整型的排序,返回排序完成的状态
return amergesort_<npy::ulong_tag>((npy_ulong *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_longlong(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理长长整型的排序,返回排序完成的状态
return amergesort_<npy::longlong_tag>((npy_longlong *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_ulonglong(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理无符号长长整型的排序,返回排序完成的状态
return amergesort_<npy::ulonglong_tag>((npy_ulonglong *)start, tosort,
num);
}
NPY_NO_EXPORT int
amergesort_half(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理半精度浮点数的排序,返回排序完成的状态
return amergesort_<npy::half_tag>((npy_half *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_float(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理单精度浮点数的排序,返回排序完成的状态
return amergesort_<npy::float_tag>((npy_float *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_double(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理双精度浮点数的排序,返回排序完成的状态
return amergesort_<npy::double_tag>((npy_double *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_longdouble(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理长双精度浮点数的排序,返回排序完成的状态
return amergesort_<npy::longdouble_tag>((npy_longdouble *)start, tosort,
num);
}
NPY_NO_EXPORT int
amergesort_cfloat(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理复数浮点数(单精度)的排序,返回排序完成的状态
return amergesort_<npy::cfloat_tag>((npy_cfloat *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_cdouble(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理复数浮点数(双精度)的排序,返回排序完成的状态
return amergesort_<npy::cdouble_tag>((npy_cdouble *)start, tosort, num);
}
NPY_NO_EXPORT int
amergesort_clongdouble(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理复数浮点数(长双精度)的排序,返回排序完成的状态
return amergesort_<npy::clongdouble_tag>((npy_clongdouble *)start, tosort,
num);
}
NPY_NO_EXPORT int
amergesort_datetime(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用模板函数 amergesort_,以处理日期时间类型的排序,返回排序完成的状态
return amergesort_<npy::datetime_tag>((npy_datetime *)start, tosort, num);
}
# 定义一个不导出的函数,使用 amergesort 算法对 timedelta 类型的数据进行排序
NPY_NO_EXPORT int
amergesort_timedelta(void *start, npy_intp *tosort, npy_intp num,
void *NPY_UNUSED(varr))
{
// 调用 amergesort_ 函数,对应 timedelta 类型的排序算法
return amergesort_<npy::timedelta_tag>((npy_timedelta *)start, tosort,
num);
}
# 定义一个不导出的函数,使用 mergesort 算法对 string 类型的数据进行排序
NPY_NO_EXPORT int
mergesort_string(void *start, npy_intp num, void *varr)
{
// 调用 string_mergesort_ 函数,对应 string 类型的 mergesort 排序算法
return string_mergesort_<npy::string_tag>((npy_char *)start, num, varr);
}
# 定义一个不导出的函数,使用 mergesort 算法对 unicode 类型的数据进行排序
NPY_NO_EXPORT int
mergesort_unicode(void *start, npy_intp num, void *varr)
{
// 调用 string_mergesort_ 函数,对应 unicode 类型的 mergesort 排序算法
return string_mergesort_<npy::unicode_tag>((npy_ucs4 *)start, num, varr);
}
# 定义一个不导出的函数,使用 amergesort 算法对 string 类型的数据进行排序
NPY_NO_EXPORT int
amergesort_string(void *v, npy_intp *tosort, npy_intp num, void *varr)
{
// 调用 string_amergesort_ 函数,对应 string 类型的 amergesort 排序算法
return string_amergesort_<npy::string_tag>((npy_char *)v, tosort, num,
varr);
}
# 定义一个不导出的函数,使用 amergesort 算法对 unicode 类型的数据进行排序
NPY_NO_EXPORT int
amergesort_unicode(void *v, npy_intp *tosort, npy_intp num, void *varr)
{
// 调用 string_amergesort_ 函数,对应 unicode 类型的 amergesort 排序算法
return string_amergesort_<npy::unicode_tag>((npy_ucs4 *)v, tosort, num,
varr);
}