NumPy 源码解析(六十四)
.\numpy\numpy\_core\src\multiarray\descriptor.h
/*
* In some API calls we wish to allow users to pass a DType class or a
* dtype instances with different meanings.
* This struct is mainly used for the argument parsing in
* `PyArray_DTypeOrDescrConverter`.
*/
typedef struct {
PyArray_DTypeMeta *dtype; // 指向 DType 类的指针
PyArray_Descr *descr; // 指向描述符实例的指针
} npy_dtype_info;
// 可选的数据类型或描述符转换函数声明
NPY_NO_EXPORT int
PyArray_DTypeOrDescrConverterOptional(PyObject *, npy_dtype_info *dt_info);
// 必需的数据类型或描述符转换函数声明
NPY_NO_EXPORT int
PyArray_DTypeOrDescrConverterRequired(PyObject *, npy_dtype_info *dt_info);
// 提取数据类型和描述符的函数声明
NPY_NO_EXPORT int
PyArray_ExtractDTypeAndDescriptor(PyArray_Descr *dtype,
PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType);
// 返回数组描述符协议类型字符串的函数声明
NPY_NO_EXPORT PyObject *arraydescr_protocol_typestr_get(
PyArray_Descr *, void *);
// 返回数组描述符协议描述符的函数声明
NPY_NO_EXPORT PyObject *arraydescr_protocol_descr_get(
PyArray_Descr *self, void *);
/*
* offset: A starting offset.
* alignment: A power-of-two alignment.
*
* This macro returns the smallest value >= 'offset'
* that is divisible by 'alignment'. Because 'alignment'
* is a power of two and integers are twos-complement,
* it is possible to use some simple bit-fiddling to do this.
*/
// 计算下一个对齐偏移量的宏定义
(((offset) + (alignment) - 1) & (-(alignment)))
// 设置类型字典的函数声明
NPY_NO_EXPORT PyObject *
array_set_typeDict(PyObject *NPY_UNUSED(ignored), PyObject *args);
// 尝试从数据类型属性转换为数组描述符的函数声明
NPY_NO_EXPORT PyArray_Descr *
_arraydescr_try_convert_from_dtype_attr(PyObject *obj);
// 检查数据类型是否采用简单的非对齐结构布局的函数声明
NPY_NO_EXPORT int
is_dtype_struct_simple_unaligned_layout(PyArray_Descr *dtype);
/*
* Filter the fields of a dtype to only those in the list of strings, ind.
*
* No type checking is performed on the input.
*
* Raises:
* ValueError - if a field is repeated
* KeyError - if an invalid field name (or any field title) is used
*/
// 将数据类型的字段过滤为指定列表中的字段的函数声明
NPY_NO_EXPORT PyArray_Descr *
arraydescr_field_subset_view(_PyArray_LegacyDescr *self, PyObject *ind);
extern NPY_NO_EXPORT char const *_datetime_strings[];
.\numpy\numpy\_core\src\multiarray\dlpack.c
/*
* 定义宏,指定使用的 NumPy API 版本,避免使用已废弃的 API
*/
/*
* 定义宏,用于指示要包含多维数组模块
*/
/*
* 清除 PY_SSIZE_T_CLEAN 宏定义,确保使用 Py_ssize_t 类型的 API
*/
/*
* 用于 NumPy 导出的 dlpack DLManagedTensor(Versioned) 的删除器函数
*/
static void
array_dlpack_deleter(DLManagedTensorVersioned *self)
{
/*
* 如果 Python 尚未初始化,则直接返回,避免操作导致错误
*/
if (!Py_IsInitialized()) {
return;
}
PyGILState_STATE state = PyGILState_Ensure(); // 获取全局解释器锁
PyArrayObject *array = (PyArrayObject *)self->manager_ctx;
/*
* 释放 self 指向的内存块,包括其分配的 shape 和 strides 内存
*/
PyMem_Free(self);
/*
* 释放 array 指向的 Python 对象,并且解除其引用计数
*/
Py_XDECREF(array);
PyGILState_Release(state); // 释放全局解释器锁
}
/* TODO: Basically same as above until dlpack v0 is removed: */
/*
* 用于未版本化的 dlpack DLManagedTensor 的删除器函数
*/
static void
array_dlpack_deleter_unversioned(DLManagedTensor *self)
{
/*
* 如果 Python 尚未初始化,则直接返回,避免操作导致错误
*/
if (!Py_IsInitialized()) {
return;
}
PyGILState_STATE state = PyGILState_Ensure(); // 获取全局解释器锁
PyArrayObject *array = (PyArrayObject *)self->manager_ctx;
/*
* 释放 self 指向的内存块
*/
PyMem_Free(self);
/*
* 释放 array 指向的 Python 对象,并且解除其引用计数
*/
Py_XDECREF(array);
PyGILState_Release(state); // 释放全局解释器锁
}
/*
* 用于 DLPack 封装的 DLManagedTensor(Versioned) 的胶囊删除器函数
*
* 这与 dlpack 规范完全一致
*/
static void
dlpack_capsule_deleter(PyObject *self)
{
/*
* 如果胶囊有效,则直接返回,避免错误操作
*/
if (PyCapsule_IsValid(self, NPY_DLPACK_VERSIONED_USED_CAPSULE_NAME)) {
return;
}
DLManagedTensorVersioned *managed =
(DLManagedTensorVersioned *)PyCapsule_GetPointer(
self, NPY_DLPACK_VERSIONED_CAPSULE_NAME);
/*
* 如果 managed 为 NULL,则输出不可解析错误信息
*/
if (managed == NULL) {
PyErr_WriteUnraisable(NULL);
return;
}
/*
* 如果 managed 指定了删除器函数,则调用该函数进行删除操作
*/
if (managed->deleter) {
managed->deleter(managed);
}
}
/* TODO: Basically same as above until dlpack v0 is removed: */
/*
* 用于未版本化的 DLPack 封装的 DLManagedTensor 的胶囊删除器函数
*/
static void
dlpack_capsule_deleter_unversioned(PyObject *self)
{
/*
* 如果胶囊有效,则直接返回,避免错误操作
*/
if (PyCapsule_IsValid(self, NPY_DLPACK_USED_CAPSULE_NAME)) {
return;
}
DLManagedTensor *managed =
(DLManagedTensor *)PyCapsule_GetPointer(self, NPY_DLPACK_CAPSULE_NAME);
/*
* 如果 managed 为 NULL,则输出不可解析错误信息
*/
if (managed == NULL) {
PyErr_WriteUnraisable(NULL);
return;
}
/*
* 如果 managed 指定了删除器函数,则调用该函数进行删除操作
*/
if (managed->deleter) {
managed->deleter(managed);
}
}
/*
* 用于作为 `from_dlpack` 中的 `base` 使用的胶囊的删除器函数
*
* 这几乎与上面的函数完全相同,作为我们数组的内部基础使用,以便重命名原始胶囊
*/
static void
array_dlpack_internal_capsule_deleter(PyObject *self)
{
DLManagedTensorVersioned *managed =
(DLManagedTensorVersioned *)PyCapsule_GetPointer(
self, NPY_DLPACK_VERSIONED_INTERNAL_CAPSULE_NAME);
if (managed == NULL) {
PyErr_WriteUnraisable(NULL);
return;
}
/*
* 根据规范,如果调用方无法提供合理的析构函数,deleter 可以是 NULL。
*/
if (managed->deleter) {
managed->deleter(managed);
/* TODO: deleter 是否允许设置 Python 异常? */
assert(!PyErr_Occurred());
}
}
/* TODO: Basically same as above until dlpack v0 is removed: */
// 定义一个静态函数,用于删除未版本化的 DLPack Capsule 对象
static void
array_dlpack_internal_capsule_deleter_unversioned(PyObject *self)
{
// 从 PyCapsule 中获取 DLManagedTensor 指针
DLManagedTensor *managed =
(DLManagedTensor *)PyCapsule_GetPointer(
self, NPY_DLPACK_INTERNAL_CAPSULE_NAME);
// 如果获取失败,记录异常信息并返回
if (managed == NULL) {
PyErr_WriteUnraisable(NULL);
return;
}
// 如果存在自定义的删除函数,则调用该函数删除 managed 指向的对象
if (managed->deleter) {
managed->deleter(managed);
assert(!PyErr_Occurred());
}
}
// This function cannot return NULL, but it can fail,
// So call PyErr_Occurred to check if it failed after
// calling it.
// 获取 PyArrayObject 对象所在的设备信息,返回 DLDevice 结构体
static DLDevice
array_get_dl_device(PyArrayObject *self) {
// 初始化返回的 DLDevice 结构体,设备类型为 CPU,设备 ID 为 0
DLDevice ret;
ret.device_type = kDLCPU;
ret.device_id = 0;
// 获取 PyArrayObject 的基础对象
PyObject *base = PyArray_BASE(self);
// 遍历基础对象链,直到找到不是 PyArrayObject 类型的对象为止
while (base != NULL && PyArray_Check(base)) {
base = PyArray_BASE((PyArrayObject *)base);
}
// 如果基础对象是有效的 DLPack Capsule,获取其 DLManagedTensor,并返回其设备信息
if (PyCapsule_IsValid(base, NPY_DLPACK_INTERNAL_CAPSULE_NAME)) {
DLManagedTensor *managed = (DLManagedTensor *)PyCapsule_GetPointer(
base, NPY_DLPACK_INTERNAL_CAPSULE_NAME);
if (managed == NULL) {
return ret;
}
return managed->dl_tensor.device;
}
// 如果基础对象是有效的版本化 DLPack Capsule,获取其 DLManagedTensorVersioned,并返回其设备信息
else if (PyCapsule_IsValid(base, NPY_DLPACK_VERSIONED_INTERNAL_CAPSULE_NAME)) {
DLManagedTensorVersioned *managed = (DLManagedTensorVersioned *)PyCapsule_GetPointer(
base, NPY_DLPACK_VERSIONED_INTERNAL_CAPSULE_NAME);
if (managed == NULL) {
return ret;
}
return managed->dl_tensor.device;
}
// 如果不是 DLPack Capsule,则返回默认的 CPU 设备信息
return ret;
}
/*
* Fill the dl_tensor struct from the `self` array.
* This struct could be versioned, but as of now is not.
*/
// 从 PyArrayObject 对象中填充 dl_tensor 结构体的信息
static int
fill_dl_tensor_information(
DLTensor *dl_tensor, PyArrayObject *self, DLDevice *result_device)
{
// 获取数组元素的字节大小、维度数、步长和形状信息
npy_intp itemsize = PyArray_ITEMSIZE(self);
int ndim = PyArray_NDIM(self);
npy_intp *strides = PyArray_STRIDES(self);
npy_intp *shape = PyArray_SHAPE(self);
// 检查数组是否是 C 连续存储的,且大小不为 1,若步长不是 itemsize 的倍数,则抛出异常
if (!PyArray_IS_C_CONTIGUOUS(self) && PyArray_SIZE(self) != 1) {
for (int i = 0; i < ndim; ++i) {
if (shape[i] != 1 && strides[i] % itemsize != 0) {
PyErr_SetString(PyExc_BufferError,
"DLPack only supports strides which are a multiple of "
"itemsize.");
return -1;
}
}
}
// 获取数组的数据类型描述对象,检查是否需要进行字节顺序调整,若需要则抛出异常
PyArray_Descr *dtype = PyArray_DESCR(self);
if (PyDataType_ISBYTESWAPPED(dtype)) {
PyErr_SetString(PyExc_BufferError,
"DLPack only supports native byte order.");
return -1;
}
// 设置 dl_tensor 结构体的数据类型信息
dl_tensor->dtype.bits = 8 * itemsize;
dl_tensor->dtype.lanes = 1;
// 如果数组的数据类型是布尔型,设置 dl_tensor 的数据类型为 kDLBool
if (PyDataType_ISBOOL(dtype)) {
dl_tensor->dtype.code = kDLBool;
}
``
else if (PyDataType_ISSIGNED(dtype)) {
// 如果数据类型是有符号整数类型,则设置管理的数据类型为 kDLInt
managed_dtype.code = kDLInt;
}
else if (PyDataType_ISUNSIGNED(dtype)) {
// 如果数据类型是无符号整数类型,则设置管理的数据类型为 kDLUInt
managed_dtype.code = kDLUInt;
}
else if (PyDataType_ISFLOAT(dtype)) {
// 如果数据类型是浮点数类型
// 我们不能确定 dtype 是 IEEE 标准或者有填充的类型
if (itemsize > 8) {
// 若数据类型字节大小大于 8,抛出异常并返回 -1
PyErr_SetString(PyExc_BufferError,
"DLPack only supports IEEE floating point types "
"without padding (longdouble typically is not IEEE).");
return -1;
}
// 设置管理的数据类型为 kDLFloat
managed_dtype.code = kDLFloat;
}
else if (PyDataType_ISCOMPLEX(dtype)) {
// 如果数据类型是复数类型
// 我们不能确定 dtype 是 IEEE 标准或者有填充的类型
if (itemsize > 16) {
// 若数据类型字节大小大于 16,抛出异常并返回 -1
PyErr_SetString(PyExc_BufferError,
"DLPack only supports IEEE floating point types "
"without padding (longdouble typically is not IEEE).");
return -1;
}
// 设置管理的数据类型为 kDLComplex
managed_dtype.code = kDLComplex;
}
else {
// 若数据类型不是上述类型之一,抛出异常并返回 -1
PyErr_SetString(PyExc_BufferError,
"DLPack only supports signed/unsigned integers, float "
"and complex dtypes.");
return -1;
}
/*
* 注意:`dlpack.h` 头文件建议/规范 `data` 必须是 256 字节对齐的。
* 我们有意忽略这一点,因为 `__dlpack__` 规定 `byte_offset` 目前必须为 0,
* 以兼容 pytorch:https://github.com/data-apis/array-api/issues/293
*
* 我们进一步假设即使没有 `byte_offset`,导出完全不对齐的数据也是可以接受的,
* 因为标准没有明确拒绝这种情况。
* 可能,从 2023 年开始,pytorch 将支持导入 `byte_offset != 0`,而 NumPy
* 可能会选择在此后使用它。届时,NumPy 可能必须使用 `byte_offset` 以符合标准
* (如头文件中所指定)!
*/
// 将 PyArray 对象的数据指针赋值给 dl_tensor 的 data
dl_tensor->data = PyArray_DATA(self);
// 设置 dl_tensor 的 byte_offset 为 0
dl_tensor->byte_offset = 0;
// 将 result_device 的值赋给 dl_tensor 的 device
dl_tensor->device = *result_device;
// 将 managed_dtype 赋给 dl_tensor 的 dtype
for (int i = 0; i < ndim; ++i) {
// 将 shape 中的第 i 个元素赋给 dl_tensor 的 shape
dl_tensor->shape[i] = shape[i];
// 在 DLPack 中,strides 是以项为单位,而在 NumPy 中是以字节为单位
// 将 strides 中的第 i 个元素除以 itemsize 后赋给 dl_tensor 的 strides
dl_tensor->strides[i] = strides[i] / itemsize;
}
// 设置 dl_tensor 的 ndim 为 ndim
dl_tensor->ndim = ndim;
// 如果 PyArray 是 C 连续存储的
// 则不需要传递 strides,将 dl_tensor 的 strides 设置为 NULL
if (PyArray_IS_C_CONTIGUOUS(self)) {
dl_tensor->strides = NULL;
}
// 再次将 dl_tensor 的 byte_offset 设置为 0
// 返回成功标志 0
return 0;
static PyObject *
create_dlpack_capsule(
PyArrayObject *self, int versioned, DLDevice *result_device, int copied)
{
int ndim = PyArray_NDIM(self);
/*
* 在结构的末尾对齐形状和步幅,需要对它们进行对齐,偏移量给出包括结构大小的形状(和步幅)的偏移量。
*/
size_t align = sizeof(int64_t);
size_t struct_size = (
versioned ? sizeof(DLManagedTensorVersioned) : sizeof(DLManagedTensor));
size_t offset = (struct_size + align - 1) / align * align;
void *ptr = PyMem_Malloc(offset + (sizeof(int64_t) * ndim * 2));
if (ptr == NULL) {
PyErr_NoMemory();
return NULL;
}
DLTensor *dl_tensor;
PyCapsule_Destructor capsule_deleter;
const char *capsule_name;
if (versioned) {
DLManagedTensorVersioned *managed = (DLManagedTensorVersioned *)ptr;
capsule_name = NPY_DLPACK_VERSIONED_CAPSULE_NAME;
capsule_deleter = (PyCapsule_Destructor)dlpack_capsule_deleter;
managed->deleter = array_dlpack_deleter;
managed->manager_ctx = self;
dl_tensor = &managed->dl_tensor;
/* 版本化的张量有额外的字段需要设置 */
managed->version.major = 1;
managed->version.minor = 0;
managed->flags = 0;
if (!PyArray_CHKFLAGS(self, NPY_ARRAY_WRITEABLE)) {
managed->flags |= DLPACK_FLAG_BITMASK_READ_ONLY;
}
if (copied) {
managed->flags |= DLPACK_FLAG_BITMASK_IS_COPIED;
}
}
else {
DLManagedTensor *managed = (DLManagedTensor *)ptr;
capsule_name = NPY_DLPACK_CAPSULE_NAME;
capsule_deleter = (PyCapsule_Destructor)dlpack_capsule_deleter_unversioned;
managed->deleter = array_dlpack_deleter_unversioned;
managed->manager_ctx = self;
dl_tensor = &managed->dl_tensor;
}
dl_tensor->shape = (int64_t *)((char *)ptr + offset);
/* 注意:如果是 C 连续的话,步幅可能会在后续设置为 NULL */
dl_tensor->strides = dl_tensor->shape + ndim;
if (fill_dl_tensor_information(dl_tensor, self, result_device) < 0) {
PyMem_Free(ptr);
return NULL;
}
PyObject *capsule = PyCapsule_New(ptr, capsule_name, capsule_deleter);
if (capsule == NULL) {
PyMem_Free(ptr);
return NULL;
}
// Capsule 持有对 self 的引用
Py_INCREF(self);
return capsule;
}
if (type == kDLCPU && id == 0) {
result_device->device_type = type;
result_device->device_id = id;
return NPY_SUCCEED;
}
PyErr_SetString(PyExc_ValueError, "unsupported device requested");
return NPY_FAIL;
/* 结束当前函数,返回一个 PyObject 类型的指针 */
NPY_NO_EXPORT PyObject *
array_dlpack(PyArrayObject *self,
PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
/* 初始化变量 stream 和 max_version,均指向 Py_None */
PyObject *stream = Py_None;
PyObject *max_version = Py_None;
/* 设置默认的数据复制模式为 NPY_COPY_IF_NEEDED */
NPY_COPYMODE copy_mode = NPY_COPY_IF_NEEDED;
/* 初始化 major_version 为 0 */
long major_version = 0;
/* 获取当前数组的设备信息,保存在 result_device 中 */
DLDevice result_device = array_get_dl_device(self);
/* 如果之前有异常发生,则直接返回 NULL */
if (PyErr_Occurred()) {
return NULL;
}
/* 准备解析参数,根据函数名、参数、关键字参数等解析 */
NPY_PREPARE_ARGPARSER;
/* 如果解析参数出错,则返回 NULL */
if (npy_parse_arguments("__dlpack__", args, len_args, kwnames,
"$stream", NULL, &stream,
"$max_version", NULL, &max_version,
"$dl_device", &device_converter, &result_device,
"$copy", &PyArray_CopyConverter, ©_mode,
NULL, NULL, NULL)) {
return NULL;
}
/* 如果 max_version 不是 Py_None,则检查其格式是否正确 */
if (max_version != Py_None) {
/* max_version 必须是一个含有两个元素的元组 */
if (!PyTuple_Check(max_version) || PyTuple_GET_SIZE(max_version) != 2) {
PyErr_SetString(PyExc_TypeError,
"max_version must be None or a tuple with two elements.");
return NULL;
}
/* 将元组中的第一个元素转换为 long 类型的 major_version */
major_version = PyLong_AsLong(PyTuple_GET_ITEM(max_version, 0));
/* 如果转换出错,则返回 NULL */
if (major_version == -1 && PyErr_Occurred()) {
return NULL;
}
}
/* 如果 stream 不是 Py_None,则抛出运行时异常 */
if (stream != Py_None) {
PyErr_SetString(PyExc_RuntimeError,
"NumPy only supports stream=None.");
return NULL;
}
/* 如果需要始终复制数组,则创建数组的副本 */
if (copy_mode == NPY_COPY_ALWAYS) {
/* TODO: 可能需要先检查导出数据类型的能力 */
/* 创建当前数组的副本,保持原有顺序 */
self = (PyArrayObject *)PyArray_NewCopy(self, NPY_KEEPORDER);
/* 如果创建副本失败,则返回 NULL */
if (self == NULL) {
return NULL;
}
}
else {
/* 增加当前数组的引用计数 */
Py_INCREF(self);
}
/* 如果 major_version 小于 1,并且当前数组是只读的,则抛出异常 */
if (major_version < 1 && !(PyArray_FLAGS(self) & NPY_ARRAY_WRITEABLE)) {
PyErr_SetString(PyExc_BufferError,
"Cannot export readonly array since signalling readonly "
"is unsupported by DLPack (supported by newer DLPack version).");
/* 减少当前数组的引用计数,并返回 NULL */
Py_DECREF(self);
return NULL;
}
/*
* TODO: DLPack 的带版本和不带版本的结构非常相似,但不兼容 ABI,
* 因此此处调用的函数需要分支(似乎不值得使用模板)。
*
* 在 NumPy 2.1 中应该弃用版本 0 的支持,并可以移除这些分支。
*/
/* 创建一个 DLPack 的封装 Capsule 对象,传入当前数组和相关参数 */
PyObject *res = create_dlpack_capsule(
self, major_version >= 1, &result_device,
copy_mode == NPY_COPY_ALWAYS);
/* 减少当前数组的引用计数 */
Py_DECREF(self);
/* 返回创建的结果对象 */
return res;
}
// 定义函数 `from_dlpack`,接受多个参数,返回一个 PyObject 指针
from_dlpack(PyObject *NPY_UNUSED(self),
PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
// 初始化变量 obj, copy, device,都指向 Py_None
PyObject *obj, *copy = Py_None, *device = Py_None;
// 使用宏 NPY_PREPARE_ARGPARSER 准备参数解析
NPY_PREPARE_ARGPARSER;
// 调用 npy_parse_arguments 解析参数,若失败返回 NULL
if (npy_parse_arguments("from_dlpack", args, len_args, kwnames,
"obj", NULL, &obj,
"$copy", NULL, ©,
"$device", NULL, &device,
NULL, NULL, NULL) < 0) {
return NULL;
}
/* 准备调用对象的 __dlpack__() 方法所需的参数 */
// 静态变量 call_kwnames 用于指定参数名列表
static PyObject *call_kwnames = NULL;
// 静态变量 dl_cpu_device_tuple 用于表示 CPU 设备
static PyObject *dl_cpu_device_tuple = NULL;
// 静态变量 max_version 用于指定最大版本号
static PyObject *max_version = NULL;
// 初始化 call_kwnames,若失败返回 NULL
if (call_kwnames == NULL) {
call_kwnames = Py_BuildValue("(sss)", "dl_device", "copy", "max_version");
if (call_kwnames == NULL) {
return NULL;
}
}
// 初始化 dl_cpu_device_tuple,若失败返回 NULL
if (dl_cpu_device_tuple == NULL) {
dl_cpu_device_tuple = Py_BuildValue("(i,i)", 1, 0);
if (dl_cpu_device_tuple == NULL) {
return NULL;
}
}
// 初始化 max_version,若失败返回 NULL
if (max_version == NULL) {
max_version = Py_BuildValue("(i,i)", 1, 0);
if (max_version == NULL) {
return NULL;
}
}
/*
* 为完整调用准备参数。始终传递 copy 并传递我们的 max_version。
* `device` 始终传递为 `None`,但如果用户提供了设备,则用 (1, 0) 替换之。
*/
// 创建参数数组 call_args
PyObject *call_args[] = {obj, Py_None, copy, max_version};
// 设置参数数量为 1,使用 PY_VECTORCALL_ARGUMENTS_OFFSET 偏移
Py_ssize_t nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
/* 若 device 不是 Py_None,则替换为 (1, 0) */
if (device != Py_None) {
/* 检查 device 是否为 CPU */
NPY_DEVICE device_request = NPY_DEVICE_CPU;
if (!PyArray_DeviceConverterOptional(device, &device_request)) {
return NULL;
}
// 断言设备请求为 NPY_DEVICE_CPU
assert(device_request == NPY_DEVICE_CPU);
// 将 call_args[1] 设置为 dl_cpu_device_tuple
call_args[1] = dl_cpu_device_tuple;
}
// 调用 PyObject_VectorcallMethod 执行对象的 __dlpack__() 方法
PyObject *capsule = PyObject_VectorcallMethod(
npy_interned_str.__dlpack__, call_args, nargsf, call_kwnames);
// 若调用失败,尝试不带任何参数(若 device 和 copy 均为 None)
if (capsule == NULL) {
if (PyErr_ExceptionMatches(PyExc_TypeError)
&& device == Py_None && copy == Py_None) {
PyErr_Clear();
capsule = PyObject_VectorcallMethod(
npy_interned_str.__dlpack__, call_args, nargsf, NULL);
}
if (capsule == NULL) {
return NULL;
}
}
// 定义变量 managed_ptr, dl_tensor, readonly, versioned,检查版本化的 Capsule
void *managed_ptr;
DLTensor dl_tensor;
int readonly;
int versioned = PyCapsule_IsValid(capsule, NPY_DLPACK_VERSIONED_CAPSULE_NAME);
if (versioned) {
managed_ptr = PyCapsule_GetPointer(capsule, NPY_DLPACK_VERSIONED_CAPSULE_NAME);
DLManagedTensorVersioned *managed = (DLManagedTensorVersioned *)managed_ptr;
if (managed == NULL) {
Py_DECREF(capsule);
return NULL;
}
if (managed->version.major > 1) {
PyErr_SetString(PyExc_BufferError,
"from_dlpack(): the exported DLPack major version is too "
"high to be imported by this version of NumPy.");
Py_DECREF(capsule);
return NULL;
}
dl_tensor = managed->dl_tensor;
readonly = (managed->flags & DLPACK_FLAG_BITMASK_READ_ONLY) != 0;
}
else {
managed_ptr = PyCapsule_GetPointer(capsule, NPY_DLPACK_CAPSULE_NAME);
DLManagedTensor *managed = (DLManagedTensor *)managed_ptr;
if (managed == NULL) {
Py_DECREF(capsule);
return NULL;
}
dl_tensor = managed->dl_tensor;
readonly = 0;
}
const int ndim = dl_tensor.ndim;
if (ndim > NPY_MAXDIMS) {
PyErr_SetString(PyExc_RuntimeError,
"maxdims of DLPack tensor is higher than the supported "
"maxdims.");
Py_DECREF(capsule);
return NULL;
}
DLDeviceType device_type = dl_tensor.device.device_type;
if (device_type != kDLCPU &&
device_type != kDLCUDAHost &&
device_type != kDLROCMHost &&
device_type != kDLCUDAManaged) {
PyErr_SetString(PyExc_RuntimeError,
"Unsupported device in DLTensor.");
Py_DECREF(capsule);
return NULL;
}
if (dl_tensor.dtype.lanes != 1) {
PyErr_SetString(PyExc_RuntimeError,
"Unsupported lanes in DLTensor dtype.");
Py_DECREF(capsule);
return NULL;
}
int typenum = -1;
const uint8_t bits = dl_tensor.dtype.bits;
const npy_intp itemsize = bits / 8;
switch (dl_tensor.dtype.code) {
case kDLBool:
if (bits == 8) {
typenum = NPY_BOOL;
}
break;
case kDLInt:
switch (bits)
{
case 8: typenum = NPY_INT8; break;
case 16: typenum = NPY_INT16; break;
case 32: typenum = NPY_INT32; break;
case 64: typenum = NPY_INT64; break;
}
break;
case kDLUInt:
switch (bits)
{
case 8: typenum = NPY_UINT8; break;
case 16: typenum = NPY_UINT16; break;
case 32: typenum = NPY_UINT32; break;
case 64: typenum = NPY_UINT64; break;
}
break;
case kDLFloat:
switch (bits)
{
case 16: typenum = NPY_FLOAT16; break;
case 32: typenum = NPY_FLOAT32; break;
case 64: typenum = NPY_FLOAT64; break;
}
break;
// 检查 DLTensor 的数据类型,并根据其位数设置 NumPy 的数据类型
case kDLComplex:
switch (bits)
{
// 对于复数,根据位数选择相应的 NumPy 数据类型
case 64: typenum = NPY_COMPLEX64; break; // 64位复数类型
case 128: typenum = NPY_COMPLEX128; break; // 128位复数类型
}
break;
}
// 如果未能识别有效的数据类型,抛出运行时错误并清理资源后返回空值
if (typenum == -1) {
PyErr_SetString(PyExc_RuntimeError,
"Unsupported dtype in DLTensor.");
Py_DECREF(capsule);
return NULL;
}
// 初始化形状和步长数组
npy_intp shape[NPY_MAXDIMS];
npy_intp strides[NPY_MAXDIMS];
// 遍历 DLTensor 的每个维度,将其形状信息复制到 NumPy 的形状数组中
for (int i = 0; i < ndim; ++i) {
shape[i] = dl_tensor.shape[i];
// 如果 DLTensor 有步长信息,将其转换为字节单位并复制到 NumPy 的步长数组中
if (dl_tensor.strides != NULL) {
strides[i] = dl_tensor.strides[i] * itemsize;
}
}
// 计算数据指针的位置,考虑 DLTensor 的偏移量
char *data = (char *)dl_tensor.data + dl_tensor.byte_offset;
// 根据 typenum 创建 NumPy 的数据描述符对象
PyArray_Descr *descr = PyArray_DescrFromType(typenum);
if (descr == NULL) {
Py_DECREF(capsule);
return NULL;
}
// 使用描述符、形状、步长等信息创建 NumPy 数组对象
PyObject *ret = PyArray_NewFromDescr(&PyArray_Type, descr, ndim, shape,
dl_tensor.strides != NULL ? strides : NULL, data, 0, NULL);
if (ret == NULL) {
Py_DECREF(capsule);
return NULL;
}
// 如果数组是只读的,清除写入标志
if (readonly) {
PyArray_CLEARFLAGS((PyArrayObject *)ret, NPY_ARRAY_WRITEABLE);
}
// 根据 versioned 标志创建 PyCapsule 对象
PyObject *new_capsule;
if (versioned) {
new_capsule = PyCapsule_New(managed_ptr,
NPY_DLPACK_VERSIONED_INTERNAL_CAPSULE_NAME,
(PyCapsule_Destructor)array_dlpack_internal_capsule_deleter);
}
else {
new_capsule = PyCapsule_New(managed_ptr,
NPY_DLPACK_INTERNAL_CAPSULE_NAME,
(PyCapsule_Destructor)array_dlpack_internal_capsule_deleter_unversioned);
}
// 如果创建 PyCapsule 失败,清理之前分配的资源并返回空值
if (new_capsule == NULL) {
Py_DECREF(capsule);
Py_DECREF(ret);
return NULL;
}
// 将新创建的 PyCapsule 对象设置为 NumPy 数组对象的基础对象
if (PyArray_SetBaseObject((PyArrayObject *)ret, new_capsule) < 0) {
Py_DECREF(capsule);
Py_DECREF(ret);
return NULL;
}
// 根据 versioned 标志设置 PyCapsule 的名称
const char *new_name = (
versioned ? NPY_DLPACK_VERSIONED_USED_CAPSULE_NAME
: NPY_DLPACK_USED_CAPSULE_NAME);
if (PyCapsule_SetName(capsule, new_name) < 0) {
Py_DECREF(capsule);
Py_DECREF(ret);
return NULL;
}
// 清理原始的 PyCapsule 对象并返回创建的 NumPy 数组对象
Py_DECREF(capsule);
return ret;
}
注释:
.\numpy\numpy\_core\src\multiarray\dragon4.c
/*
* 版权所有(c)2014 Ryan Juckett
*
* 根据以下条件,免费授予任何获得本软件及相关文档副本的人:
* 无限制地处理本软件的权限,包括但不限于使用、复制、修改、合并、
* 发布、分发、再许可和/或销售本软件的副本;以及
* 允许使用本软件的人员,但需满足以下条件:
*
* 上述版权声明和本许可声明应包含在
* 所有或实质部分的本软件副本中。
*
* 本软件按“原样”提供,无任何形式的明示或默示保证,
* 包括但不限于适销性保证、特定用途的适用性和非侵权性保证。
* 在任何情况下,作者或版权持有人均无法承担
* 由于使用本软件或与其使用相关的其他操作而产生的任何索赔、损害或其他
* 责任。
*/
/*
* 本文件包含了 Ryan Juckett 的 Dragon4 实现的修改版本,
* 从 https://www.ryanjuckett.com 获得,
* 该版本已从 C++ 移植到 C,并具有特定于在 numpy 中打印浮点数的修改。
*
* Ryan Juckett 的原始代码使用 Zlib 许可证;他允许 numpy
* 将其包含在 MIT 许可证下。
*/
/*
* 返回一个 64 位无符号整数,其中低 n 位被置为 1。
*/
static inline npy_uint64
bitmask_u64(npy_uint32 n)
{
return ~(~((npy_uint64)0) << n);
}
/*
* 返回一个 32 位无符号整数,其中低 n 位被置为 1。
*/
static inline npy_uint32
bitmask_u32(npy_uint32 n)
{
return ~(~((npy_uint32)0) << n);
}
/*
* 获取 32 位无符号整数的以 2 为底的对数。
* 参考:https://graphics.stanford.edu/~seander/bithacks.html
*/
static npy_uint32
LogBase2_32(npy_uint32 val)
{
static const npy_uint8 logTable[256] =
{
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};
npy_uint32 temp;
// 将 val 的高位向右移动 24 位,并且判断结果是否非零
temp = val >> 24;
if (temp) {
// 如果 temp 非零,返回对应的 logTable 值加上 24
return 24 + logTable[temp];
}
// 将 val 的高位向右移动 16 位,并且判断结果是否非零
temp = val >> 16;
if (temp) {
// 如果 temp 非零,返回对应的 logTable 值加上 16
return 16 + logTable[temp];
}
// 将 val 的高位向右移动 8 位,并且判断结果是否非零
temp = val >> 8;
if (temp) {
// 如果 temp 非零,返回对应的 logTable 值加上 8
return 8 + logTable[temp];
}
// 如果以上条件都不满足,则直接返回 logTable[val]
return logTable[val];
}
static npy_uint32
LogBase2_64(npy_uint64 val)
{
npy_uint64 temp;
// 取输入值的高 32 位
temp = val >> 32;
// 如果高 32 位不为零,则返回 32 加上低 32 位的 LogBase2_32 值
if (temp) {
return 32 + LogBase2_32((npy_uint32)temp);
}
// 否则,返回低 32 位的 LogBase2_32 值
return LogBase2_32((npy_uint32)val);
}
static npy_uint32
LogBase2_128(npy_uint64 hi, npy_uint64 lo)
{
// 如果高位不为零,返回 64 加上高 64 位的 LogBase2_64 值
if (hi) {
return 64 + LogBase2_64(hi);
}
// 否则,返回低 64 位的 LogBase2_64 值
return LogBase2_64(lo);
}
/*
* Maximum number of 32 bit blocks needed in high precision arithmetic to print
* out 128 bit IEEE floating point values. 1023 chosen to be large enough for
* 128 bit floats, and BigInt is exactly 4kb (nice for page/cache?)
*/
// 定义在高精度算术中打印 128 位 IEEE 浮点值所需的最大 32 位块数
/*
* This structure stores a high precision unsigned integer. It uses a buffer of
* 32 bit integer blocks along with a length. The lowest bits of the integer
* are stored at the start of the buffer and the length is set to the minimum
* value that contains the integer. Thus, there are never any zero blocks at
* the end of the buffer.
*/
// 定义存储高精度无符号整数的结构体 BigInt
typedef struct BigInt {
npy_uint32 length; // 整数的长度
npy_uint32 blocks[c_BigInt_MaxBlocks]; // 32 位整数块的数组
} BigInt;
/*
* Dummy implementation of a memory manager for BigInts. Currently, only
* supports a single call to Dragon4, but that is OK because Dragon4
* does not release the GIL.
*
* We try to raise an error anyway if dragon4 re-enters, and this code serves
* as a placeholder if we want to make it re-entrant in the future.
*
* Each call to dragon4 uses 7 BigInts.
*/
// 用于 BigInt 的内存管理器的虚拟实现。当前仅支持 Dragon4 的单次调用。
// Dragon4 不释放 GIL,因此这是可以的。
// 如果 dragon4 重入,我们尝试引发错误,这段代码在未来可能变为可重入的占位符。
// 每次 dragon4 调用使用 7 个 BigInt。
typedef struct {
BigInt bigints[BIGINT_DRAGON4_GROUPSIZE]; // 用于 Dragon4 的 BigInt 数组
char repr[16384]; // 字符串表示形式的缓冲区
} Dragon4_Scratch;
static int _bigint_static_in_use = 0; // 静态 BigInt 使用标志
static Dragon4_Scratch _bigint_static; // 静态 Dragon4_Scratch 实例
static Dragon4_Scratch*
get_dragon4_bigint_scratch(void) {
// 这个测试和设置不是线程安全的,但由于有 GIL,不会有问题
if (_bigint_static_in_use) {
PyErr_SetString(PyExc_RuntimeError,
"numpy float printing code is not re-entrant. "
"Ping the devs to fix it.");
return NULL;
}
_bigint_static_in_use = 1;
// 在这个虚拟实现中,我们只返回静态分配
return &_bigint_static;
}
static void
free_dragon4_bigint_scratch(Dragon4_Scratch *mem){
_bigint_static_in_use = 0;
}
/* Copy integer */
static void
BigInt_Copy(BigInt *dst, const BigInt *src)
{
npy_uint32 length = src->length; // 获取源 BigInt 的长度
npy_uint32 * dstp = dst->blocks; // 目标 BigInt 的块指针
const npy_uint32 *srcp; // 源 BigInt 的块指针
// 循环复制源 BigInt 的每个块到目标 BigInt
for (srcp = src->blocks; srcp != src->blocks + length; ++dstp, ++srcp) {
*dstp = *srcp;
}
dst->length = length; // 设置目标 BigInt 的长度
}
/* Basic type accessors */
static void
BigInt_Set_uint64(BigInt *i, npy_uint64 val)
{
// 如果输入值大于 32 位掩码,则分别设置低 32 位和高 32 位块,并设置长度为 2
if (val > bitmask_u64(32)) {
i->blocks[0] = val & bitmask_u64(32);
i->blocks[1] = (val >> 32) & bitmask_u64(32);
i->length = 2;
}
}
else if (val != 0) {
i->blocks[0] = val & bitmask_u64(32);
i->length = 1;
}
else {
i->length = 0;
}
}
defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE) || \
defined(HAVE_LDOUBLE_IEEE_QUAD_LE) || \
defined(HAVE_LDOUBLE_IEEE_QUAD_BE))
static void
BigInt_Set_2x_uint64(BigInt *i, npy_uint64 hi, npy_uint64 lo)
{
// 根据 hi 和 lo 的值设置 BigInt 的长度
if (hi > bitmask_u64(32)) {
i->length = 4;
}
else if (hi != 0) {
i->length = 3;
}
else if (lo > bitmask_u64(32)) {
i->length = 2;
}
else if (lo != 0) {
i->length = 1;
}
else {
i->length = 0;
}
/* Note deliberate fallthrough in this switch */
// 根据 BigInt 的长度设置对应的 blocks 数组值
switch (i->length) {
case 4:
i->blocks[3] = (hi >> 32) & bitmask_u64(32);
case 3:
i->blocks[2] = hi & bitmask_u64(32);
case 2:
i->blocks[1] = (lo >> 32) & bitmask_u64(32);
case 1:
i->blocks[0] = lo & bitmask_u64(32);
}
}
static void
BigInt_Set_uint32(BigInt *i, npy_uint32 val)
{
// 设置 BigInt 为一个 32 位无符号整数
if (val != 0) {
i->blocks[0] = val;
i->length = 1;
}
else {
i->length = 0;
}
}
/*
* Returns 1 if the value is zero
*/
static int
BigInt_IsZero(const BigInt *i)
{
// 判断 BigInt 是否为零
return i->length == 0;
}
/*
* Returns 1 if the value is even
*/
static int
BigInt_IsEven(const BigInt *i)
{
// 判断 BigInt 是否为偶数
return (i->length == 0) || ((i->blocks[0] % 2) == 0);
}
/*
* Returns 0 if (lhs = rhs), negative if (lhs < rhs), positive if (lhs > rhs)
*/
static npy_int32
BigInt_Compare(const BigInt *lhs, const BigInt *rhs)
{
int i;
// 比较两个 BigInt 的大小
/* A bigger length implies a bigger number. */
npy_int32 lengthDiff = lhs->length - rhs->length;
if (lengthDiff != 0) {
return lengthDiff;
}
/* Compare blocks one by one from high to low. */
// 逐个比较 blocks 数组中的值,从高位到低位
for (i = lhs->length - 1; i >= 0; --i) {
if (lhs->blocks[i] == rhs->blocks[i]) {
continue;
}
else if (lhs->blocks[i] > rhs->blocks[i]) {
return 1;
}
else {
return -1;
}
}
/* no blocks differed */
// 没有发现不同的 blocks,说明两个数相等
return 0;
}
/* result = lhs + rhs */
static void
BigInt_Add(BigInt *result, const BigInt *lhs, const BigInt *rhs)
{
/* determine which operand has the smaller length */
// 确定哪个操作数长度较小
const BigInt *large, *small;
npy_uint64 carry = 0;
const npy_uint32 *largeCur, *smallCur, *largeEnd, *smallEnd;
npy_uint32 *resultCur;
if (lhs->length < rhs->length) {
small = lhs;
large = rhs;
}
else {
small = rhs;
large = lhs;
}
/* The output will be at least as long as the largest input */
// 结果的长度至少与较大输入的长度一样长
result->length = large->length;
/* Add each block and add carry the overflow to the next block */
// 每个块相加,并将溢出的 carry 添加到下一个块中
largeCur = large->blocks;
largeEnd = largeCur + large->length;
smallCur = small->blocks;
smallEnd = smallCur + small->length;
resultCur = result->blocks;
}
while (smallCur != smallEnd) {
npy_uint64 sum = carry + (npy_uint64)(*largeCur) +
(npy_uint64)(*smallCur);
carry = sum >> 32;
*resultCur = sum & bitmask_u64(32);
++largeCur;
++smallCur;
++resultCur;
}
while (largeCur != largeEnd) {
npy_uint64 sum = carry + (npy_uint64)(*largeCur);
carry = sum >> 32;
(*resultCur) = sum & bitmask_u64(32);
++largeCur;
++resultCur;
}
if (carry != 0) {
DEBUG_ASSERT(carry == 1);
DEBUG_ASSERT((npy_uint32)(resultCur - result->blocks) ==
large->length && (large->length < c_BigInt_MaxBlocks));
*resultCur = 1;
result->length = large->length + 1;
}
else {
result->length = large->length;
}
/*
* result = lhs * rhs
*/
static void
BigInt_Multiply(BigInt *result, const BigInt *lhs, const BigInt *rhs)
{
const BigInt *large; // 指向较大的 BigInt 结构体
const BigInt *small; // 指向较小的 BigInt 结构体
npy_uint32 maxResultLen; // 结果数组的最大长度
npy_uint32 *cur, *end, *resultStart; // 当前操作的指针和结束指针
const npy_uint32 *smallCur; // 指向小数位数组的当前指针
DEBUG_ASSERT(result != lhs && result != rhs); // 断言结果不等于乘数和被乘数
/* determine which operand has the smaller length */
if (lhs->length < rhs->length) {
small = lhs; // 左操作数较短
large = rhs; // 右操作数较长
}
else {
small = rhs; // 右操作数较短
large = lhs; // 左操作数较长
}
/* set the maximum possible result length */
maxResultLen = large->length + small->length; // 计算结果数组的最大长度
DEBUG_ASSERT(maxResultLen <= c_BigInt_MaxBlocks); // 断言结果长度不超过最大允许长度
/* clear the result data */
for (cur = result->blocks, end = cur + maxResultLen; cur != end; ++cur) {
*cur = 0; // 将结果数组清零
}
/* perform standard long multiplication for each small block */
resultStart = result->blocks; // 结果数组的起始位置
for (smallCur = small->blocks;
smallCur != small->blocks + small->length;
++smallCur, ++resultStart) {
/*
* if non-zero, multiply against all the large blocks and add into the
* result
*/
const npy_uint32 multiplier = *smallCur; // 当前小数位的值作为乘数
if (multiplier != 0) { // 如果乘数不为零
const npy_uint32 *largeCur = large->blocks; // 大数位的起始位置
npy_uint32 *resultCur = resultStart; // 结果数组的当前位置
npy_uint64 carry = 0; // 进位初始化为零
do {
npy_uint64 product = (*resultCur) +
(*largeCur)*(npy_uint64)multiplier + carry; // 计算乘法并加上进位
carry = product >> 32; // 计算新的进位
*resultCur = product & bitmask_u64(32); // 将低32位作为新的结果
++largeCur; // 移动到下一个大数位
++resultCur; // 移动到下一个结果位置
} while(largeCur != large->blocks + large->length);
DEBUG_ASSERT(resultCur < result->blocks + maxResultLen); // 断言结果位置不超过最大允许长度
*resultCur = (npy_uint32)(carry & bitmask_u64(32)); // 将最终进位写入结果数组
}
}
/* check if the terminating block has no set bits */
if (maxResultLen > 0 && result->blocks[maxResultLen - 1] == 0) {
result->length = maxResultLen - 1; // 如果最高位为零,则结果长度减一
}
else {
result->length = maxResultLen; // 否则结果长度为计算得到的最大长度
}
}
/* result = lhs * rhs */
static void
BigInt_Multiply_int(BigInt *result, const BigInt *lhs, npy_uint32 rhs)
{
/* perform long multiplication */
npy_uint32 carry = 0; // 进位初始化为零
npy_uint32 *resultCur = result->blocks; // 结果数组的当前位置
const npy_uint32 *pLhsCur = lhs->blocks; // 左操作数的当前位置
const npy_uint32 *pLhsEnd = lhs->blocks + lhs->length; // 左操作数的结束位置
for (; pLhsCur != pLhsEnd; ++pLhsCur, ++resultCur) {
npy_uint64 product = (npy_uint64)(*pLhsCur) * rhs + carry; // 计算乘法并加上进位
*resultCur = (npy_uint32)(product & bitmask_u64(32)); // 将低32位作为新的结果
carry = product >> 32; // 计算新的进位
}
/* if there is a remaining carry, grow the array */
if (carry != 0) { // 如果还有进位
/* grow the array */
DEBUG_ASSERT(lhs->length + 1 <= c_BigInt_MaxBlocks); // 断言结果长度加一不超过最大允许长度
*resultCur = (npy_uint32)carry; // 将最终进位写入结果数组
result->length = lhs->length + 1; // 结果长度加一
}
else {
result->length = lhs->length; // 结果长度保持不变
}
}
/* result = in * 2 */
static void
BigInt_Multiply2(BigInt *result, const BigInt *in)
{
/* shift all the blocks by one */
npy_uint32 carry = 0;
npy_uint32 *resultCur = result->blocks; /* 指向结果 BigInt 结构体中的数据块 */
const npy_uint32 *pLhsCur = in->blocks; /* 指向输入 BigInt 结构体中的数据块 */
const npy_uint32 *pLhsEnd = in->blocks + in->length; /* 指向输入 BigInt 结构体数据块末尾的下一个位置 */
for ( ; pLhsCur != pLhsEnd; ++pLhsCur, ++resultCur) {
npy_uint32 cur = *pLhsCur; /* 当前输入数据块的值 */
*resultCur = (cur << 1) | carry; /* 将当前输入数据块的值左移一位,并加上进位,存入结果数据块中 */
carry = cur >> 31; /* 计算当前输入数据块的最高位作为下一次的进位 */
}
if (carry != 0) {
/* grow the array */
DEBUG_ASSERT(in->length + 1 <= c_BigInt_MaxBlocks); /* 断言,确保结果数组不会超过最大块数 */
*resultCur = carry; /* 如果最高位有进位,则将进位加入到结果数组的最后一块 */
result->length = in->length + 1; /* 更新结果 BigInt 结构体的长度 */
}
else {
result->length = in->length; /* 没有进位,则结果长度与输入相同 */
}
}
/* result = result * 2 */
static void
BigInt_Multiply2_inplace(BigInt *result)
{
/* shift all the blocks by one */
npy_uint32 carry = 0;
npy_uint32 *cur = result->blocks; /* 指向结果 BigInt 结构体中的数据块 */
npy_uint32 *end = result->blocks + result->length; /* 指向结果 BigInt 结构体数据块末尾的下一个位置 */
for ( ; cur != end; ++cur) {
npy_uint32 tmpcur = *cur; /* 当前结果数据块的值 */
*cur = (tmpcur << 1) | carry; /* 将当前结果数据块的值左移一位,并加上进位,存回结果数据块中 */
carry = tmpcur >> 31; /* 计算当前结果数据块的最高位作为下一次的进位 */
}
if (carry != 0) {
/* grow the array */
DEBUG_ASSERT(result->length + 1 <= c_BigInt_MaxBlocks); /* 断言,确保结果数组不会超过最大块数 */
*cur = carry; /* 如果最高位有进位,则将进位加入到结果数组的最后一块 */
++result->length; /* 更新结果 BigInt 结构体的长度 */
}
}
/* result = result * 10 */
static void
BigInt_Multiply10(BigInt *result)
{
/* multiply all the blocks */
npy_uint64 carry = 0;
npy_uint32 *cur = result->blocks; /* 指向结果 BigInt 结构体中的数据块 */
npy_uint32 *end = result->blocks + result->length; /* 指向结果 BigInt 结构体数据块末尾的下一个位置 */
for ( ; cur != end; ++cur) {
npy_uint64 product = (npy_uint64)(*cur) * 10ull + carry; /* 计算当前结果数据块乘以 10 后的结果,并加上进位 */
(*cur) = (npy_uint32)(product & bitmask_u64(32)); /* 将结果截断为 32 位,并存回当前结果数据块中 */
carry = product >> 32; /* 计算除去 32 位后的进位 */
}
if (carry != 0) {
/* grow the array */
DEBUG_ASSERT(result->length + 1 <= c_BigInt_MaxBlocks); /* 断言,确保结果数组不会超过最大块数 */
*cur = (npy_uint32)carry; /* 如果还有进位,则将进位加入到结果数组的最后一块 */
++result->length; /* 更新结果 BigInt 结构体的长度 */
}
}
static npy_uint32 g_PowerOf10_U32[] =
{
1, /* 10 ^ 0 */
10, /* 10 ^ 1 */
100, /* 10 ^ 2 */
1000, /* 10 ^ 3 */
10000, /* 10 ^ 4 */
100000, /* 10 ^ 5 */
1000000, /* 10 ^ 6 */
10000000, /* 10 ^ 7 */
};
/*
* Note: This has a lot of wasted space in the big integer structures of the
* early table entries. It wouldn't be terribly hard to make the multiply
* function work on integer pointers with an array length instead of
* the BigInt struct which would allow us to store a minimal amount of
* data here.
*/
static BigInt g_PowerOf10_Big[] =
{
/* 10 ^ 8 */
{ 1, { 100000000 } }, /* 以 BigInt 结构体表示的 10^8 */
/* 10 ^ 16 */
{ 2, { 0x6fc10000, 0x002386f2 } }, /* 以 BigInt 结构体表示的 10^16 */
/* 10 ^ 32 */
{ 4, { 0x00000000, 0x85acef81, 0x2d6d415b, 0x000004ee, } }, /* 以 BigInt 结构体表示的 10^32 */
/* 10 ^ 64 */
{ 7, { 0x00000000, 0x00000000, 0xbf6a1f01, 0x6e38ed64, 0xdaa797ed,
0xe93ff9f4, 0x00184f03, } }, /* 以 BigInt 结构体表示的 10^64 */
};
{ 14, { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2e953e01,
0x03df9909, 0x0f1538fd, 0x2374e42f, 0xd3cff5ec, 0xc404dc08,
0xbccdb0da, 0xa6337f19, 0xe91f2603, 0x0000024e, } },
/* 10 ^ 256 */
{ 27, { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x982e7c01, 0xbed3875b,
0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70, 0xd595d80f,
0x26b2716e, 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e,
0xcc5573c0, 0x65f9ef17, 0x55bc28f2, 0x80dcc7f7, 0xf46eeddc,
0x5fdcefce, 0x000553f7, } },
/* 10 ^ 512 */
{ 54, { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0xfc6cf801, 0x77f27267, 0x8f9546dc, 0x5d96976f,
0xb83a8a97, 0xc31e1ad9, 0x46c40513, 0x94e65747, 0xc88976c1,
0x4475b579, 0x28f8733b, 0xaa1da1bf, 0x703ed321, 0x1e25cfea,
0xb21a2f22, 0xbc51fb2e, 0x96e14f5d, 0xbfa3edac, 0x329c57ae,
0xe7fc7153, 0xc3fc0695, 0x85a91924, 0xf95f635e, 0xb2908ee0,
0x93abade4, 0x1366732a, 0x9449775c, 0x69be5b0e, 0x7343afac,
0xb099bc81, 0x45a71d46, 0xa2699748, 0x8cb07303, 0x8a0b1f13,
0x8cab8a97, 0xc1d238d9, 0x633415d4, 0x0000001c, } },
/* 10 ^ 1024 */
注释:
这段代码定义了三个数据结构,每个结构包含一个整数和一个数组。每个整数注释描述了它所代表的幂次方,而数组则包含了十进制数 10 的该幂次方的大整数表示。
{ 107, { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x2919f001, 0xf55b2b72, 0x6e7c215b,
0x1ec29f86, 0x991c4e87, 0x15c51a88, 0x140ac535, 0x4c7d1e1a,
0xcc2cd819, 0x0ed1440e, 0x896634ee, 0x7de16cfb, 0x1e43f61f,
0x9fce837d, 0x231d2b9c, 0x233e55c7, 0x65dc60d7, 0xf451218b,
0x1c5cd134, 0xc9635986, 0x922bbb9f, 0xa7e89431, 0x9f9f2a07,
0x62be695a, 0x8e1042c4, 0x045b7a74, 0x1abe1de3, 0x8ad822a5,
0xba34c411, 0xd814b505, 0xbf3fdeb3, 0x8fc51a16, 0xb1b896bc,
0xf56deeec, 0x31fb6bfd, 0xb6f4654b, 0x101a3616, 0x6b7595fb,
0xdc1a47fe, 0x80d98089, 0x80bda5a5, 0x9a202882, 0x31eb0f66,
0xfc8f1f90, 0x976a3310, 0xe26a7b7e, 0xdf68368a, 0x3ce3a0b8,
0x8e4262ce, 0x75a351a2, 0x6cb0b6c9, 0x44597583, 0x31b5653f,
0xc356e38a, 0x35faaba6, 0x0190fba0, 0x9fc4ed52, 0x88bc491b,
0x1640114a, 0x005b8041, 0xf4f3235e, 0x1e8d4649, 0x36a8de06,
0x73c55349, 0xa7e6bd2a, 0xc1a6970c, 0x47187094, 0xd2db49ef,
0x926c3f5b, 0xae6209d4, 0x2d433949, 0x34f4a3c6, 0xd4305d94,
0xd9d61a05, 0x00000325, } },
/* 10 ^ 2048 */
/* 10 ^ 4096 */
注释:
};
/* result = 10^exponent */
static void
BigInt_Pow10(BigInt *result, npy_uint32 exponent, BigInt *temp)
{
/* use two temporary values to reduce large integer copy operations */
BigInt *curTemp = result; // 当前临时变量指向结果
BigInt *pNextTemp = temp; // 下一个临时变量指向传入的临时变量
npy_uint32 smallExponent; // 小指数值
npy_uint32 tableIdx = 0; // 表索引
/* make sure the exponent is within the bounds of the lookup table data */
DEBUG_ASSERT(exponent < 8192); // 断言,确保指数在查找表数据范围内
/*
* initialize the result by looking up a 32-bit power of 10 corresponding to
* the first 3 bits
*/
smallExponent = exponent & bitmask_u32(3); // 取指数的低3位作为小指数
BigInt_Set_uint32(curTemp, g_PowerOf10_U32[smallExponent]); // 使用32位整数表中的值初始化结果
/* remove the low bits that we used for the 32-bit lookup table */
exponent >>= 3; // 右移3位,去除已经使用过的低3位
/* while there are remaining bits in the exponent to be processed */
while (exponent != 0) {
/* if the current bit is set, multiply by this power of 10 */
if (exponent & 1) {
BigInt *pSwap;
/* multiply into the next temporary */
BigInt_Multiply(pNextTemp, curTemp, &g_PowerOf10_Big[tableIdx]);
/* swap to the next temporary */
pSwap = curTemp;
curTemp = pNextTemp;
pNextTemp = pSwap;
}
/* advance to the next bit */
++tableIdx;
exponent >>= 1; // 右移一位,处理下一位指数
}
/* output the result */
if (curTemp != result) {
BigInt_Copy(result, curTemp); // 如果当前临时变量不是结果,则拷贝到结果
}
}
/* in = in * 10^exponent */
static void
BigInt_MultiplyPow10(BigInt *in, npy_uint32 exponent, BigInt *temp)
{
/* use two temporary values to reduce large integer copy operations */
BigInt *curTemp, *pNextTemp;
npy_uint32 smallExponent;
npy_uint32 tableIdx = 0;
/* make sure the exponent is within the bounds of the lookup table data */
DEBUG_ASSERT(exponent < 8192); // 断言,确保指数在查找表数据范围内
/*
* initialize the result by looking up a 32-bit power of 10 corresponding to
* the first 3 bits
*/
smallExponent = exponent & bitmask_u32(3); // 取指数的低3位作为小指数
if (smallExponent != 0) {
BigInt_Multiply_int(temp, in, g_PowerOf10_U32[smallExponent]); // 使用32位整数表中的值乘以输入值,并存入临时变量
curTemp = temp;
pNextTemp = in;
}
else {
curTemp = in;
pNextTemp = temp;
}
/* remove the low bits that we used for the 32-bit lookup table */
exponent >>= 3; // 右移3位,去除已经使用过的低3位
/* while there are remaining bits in the exponent to be processed */
while (exponent != 0) {
/* if the current bit is set, multiply by this power of 10 */
if (exponent & 1) {
BigInt *pSwap;
/* multiply into the next temporary */
BigInt_Multiply(pNextTemp, curTemp, &g_PowerOf10_Big[tableIdx]);
/* swap to the next temporary */
pSwap = curTemp;
curTemp = pNextTemp;
pNextTemp = pSwap;
}
/* advance to the next bit */
++tableIdx;
exponent >>= 1; // 右移一位,处理下一位指数
}
/* output the result */
if (curTemp != in){
BigInt_Copy(in, curTemp); // 如果当前临时变量不是输入值,则拷贝到输入值
}
}
/*
* result = 2^exponent
* Computes the power of 2 raised to the exponent for a given BigInt.
*/
static inline void
BigInt_Pow2(BigInt *result, npy_uint32 exponent)
{
npy_uint32 bitIdx;
npy_uint32 blockIdx = exponent / 32; // Determine the block index based on exponent
npy_uint32 i;
DEBUG_ASSERT(blockIdx < c_BigInt_MaxBlocks); // Assert that blockIdx is within valid range
// Initialize blocks up to blockIdx to 0
for (i = 0; i <= blockIdx; ++i) {
result->blocks[i] = 0;
}
result->length = blockIdx + 1; // Set the length of the result BigInt
bitIdx = (exponent % 32); // Determine the bit index within the last block
result->blocks[blockIdx] |= ((npy_uint32)1 << bitIdx); // Set the corresponding bit in the last block
}
/*
* This function will divide two large numbers under the assumption that the
* result is within the range [0,10) and the input numbers have been shifted
* to satisfy:
* - The highest block of the divisor is greater than or equal to 8 such that
* there is enough precision to make an accurate first guess at the quotient.
* - The highest block of the divisor is less than the maximum value on an
* unsigned 32-bit integer such that we can safely increment without overflow.
* - The dividend does not contain more blocks than the divisor such that we
* can estimate the quotient by dividing the equivalently placed high blocks.
*
* quotient = floor(dividend / divisor)
* remainder = dividend - quotient*divisor
*
* dividend is updated to be the remainder and the quotient is returned.
*/
static npy_uint32
BigInt_DivideWithRemainder_MaxQuotient9(BigInt *dividend, const BigInt *divisor)
{
npy_uint32 length, quotient;
const npy_uint32 *finalDivisorBlock;
npy_uint32 *finalDividendBlock;
/*
* Check that the divisor has been correctly shifted into range and that it
* is not smaller than the dividend in length.
*/
DEBUG_ASSERT(!divisor->length == 0 &&
divisor->blocks[divisor->length-1] >= 8 &&
divisor->blocks[divisor->length-1] < bitmask_u64(32) &&
dividend->length <= divisor->length);
/*
* If the dividend is smaller than the divisor, the quotient is zero and the
* divisor is already the remainder.
*/
length = divisor->length;
if (dividend->length < divisor->length) {
return 0; // Return zero if dividend is smaller than divisor
}
finalDivisorBlock = divisor->blocks + length - 1;
finalDividendBlock = dividend->blocks + length - 1;
/*
* Compute an estimated quotient based on the high block value. This will
* either match the actual quotient or undershoot by one.
*/
quotient = *finalDividendBlock / (*finalDivisorBlock + 1);
DEBUG_ASSERT(quotient <= 9); // Assert that the estimated quotient is within [0, 9]
/* Divide out the estimated quotient */
}
if (quotient != 0) {
/*
* 如果商不为零,则执行以下操作:
* dividend = dividend - divisor * quotient
*/
const npy_uint32 *divisorCur = divisor->blocks;
npy_uint32 *dividendCur = dividend->blocks;
npy_uint64 borrow = 0;
npy_uint64 carry = 0;
do {
npy_uint64 difference, product;
product = (npy_uint64)*divisorCur * (npy_uint64)quotient + carry;
carry = product >> 32;
difference = (npy_uint64)*dividendCur
- (product & bitmask_u64(32)) - borrow;
borrow = (difference >> 32) & 1;
*dividendCur = difference & bitmask_u64(32);
++divisorCur;
++dividendCur;
} while(divisorCur <= finalDivisorBlock);
/*
* 从 dividend 中移除所有前导的零块
*/
while (length > 0 && dividend->blocks[length - 1] == 0) {
--length;
}
dividend->length = length;
}
/*
* 如果 dividend 仍然大于等于 divisor,则说明估算的商有误。
* 此时需要增加商的值并再次减去一个 divisor 从 dividend 中。
*/
if (BigInt_Compare(dividend, divisor) >= 0) {
/*
* dividend = dividend - divisor
*/
const npy_uint32 *divisorCur = divisor->blocks;
npy_uint32 *dividendCur = dividend->blocks;
npy_uint64 borrow = 0;
++quotient;
do {
npy_uint64 difference = (npy_uint64)*dividendCur
- (npy_uint64)*divisorCur - borrow;
borrow = (difference >> 32) & 1;
*dividendCur = difference & bitmask_u64(32);
++divisorCur;
++dividendCur;
} while(divisorCur <= finalDivisorBlock);
/*
* 从 dividend 中移除所有前导的零块
*/
while (length > 0 && dividend->blocks[length - 1] == 0) {
--length;
}
dividend->length = length;
}
// 返回最终的商
return quotient;
/* 左移大整数对象中的位,结果存储在 result 中 */
static void
BigInt_ShiftLeft(BigInt *result, npy_uint32 shift)
{
/* 计算需要移动的块数和位数 */
npy_uint32 shiftBlocks = shift / 32;
npy_uint32 shiftBits = shift % 32;
/* 从高到低处理块,以便可以安全地原地处理 */
const npy_uint32 *pInBlocks = result->blocks;
npy_int32 inLength = result->length;
npy_uint32 *pInCur, *pOutCur;
/* 断言确保移动后块的数量不超过最大限制 */
DEBUG_ASSERT(inLength + shiftBlocks < c_BigInt_MaxBlocks);
DEBUG_ASSERT(shift != 0);
/* 检查移动是否按块对齐 */
if (shiftBits == 0) {
npy_uint32 i;
/* 从高到低复制块 */
for (pInCur = result->blocks + result->length,
pOutCur = pInCur + shiftBlocks;
pInCur >= pInBlocks;
--pInCur, --pOutCur) {
*pOutCur = *pInCur;
}
/* 将剩余低块清零 */
for (i = 0; i < shiftBlocks; ++i) {
result->blocks[i] = 0;
}
result->length += shiftBlocks;
}
/* 否则需要移动部分块 */
else {
npy_uint32 i;
npy_int32 inBlockIdx = inLength - 1;
npy_uint32 outBlockIdx = inLength + shiftBlocks;
/* 输出初始块 */
const npy_uint32 lowBitsShift = (32 - shiftBits);
npy_uint32 highBits = 0;
npy_uint32 block = result->blocks[inBlockIdx];
npy_uint32 lowBits = block >> lowBitsShift;
/* 设置长度以容纳移动后的块 */
DEBUG_ASSERT(outBlockIdx < c_BigInt_MaxBlocks);
result->length = outBlockIdx + 1;
while (inBlockIdx > 0) {
result->blocks[outBlockIdx] = highBits | lowBits;
highBits = block << shiftBits;
--inBlockIdx;
--outBlockIdx;
block = result->blocks[inBlockIdx];
lowBits = block >> lowBitsShift;
}
/* 输出最终块 */
DEBUG_ASSERT(outBlockIdx == shiftBlocks + 1);
result->blocks[outBlockIdx] = highBits | lowBits;
result->blocks[outBlockIdx - 1] = block << shiftBits;
/* 将剩余低块清零 */
for (i = 0; i < shiftBlocks; ++i) {
result->blocks[i] = 0;
}
/* 检查最后一个块是否没有设置位 */
if (result->blocks[result->length - 1] == 0) {
--result->length;
}
}
}
/* Dragon4 算法的实现,生成十进制数字字符串 */
static npy_uint32
Dragon4(BigInt *bigints, const npy_int32 exponent,
const npy_uint32 mantissaBit, const npy_bool hasUnequalMargins,
const DigitMode digitMode, const CutoffMode cutoffMode,
npy_int32 cutoff_max, npy_int32 cutoff_min, char *pOutBuffer,
npy_uint32 bufferSize, npy_int32 *pOutExponent)
{
char *curDigit = pOutBuffer;
/*
* We compute values in integer format by rescaling as
* mantissa = scaledValue / scale
* marginLow = scaledMarginLow / scale
* marginHigh = scaledMarginHigh / scale
* Here, marginLow and marginHigh represent 1/2 of the distance to the next
* floating point value above/below the mantissa.
*
* scaledMarginHigh will point to scaledMarginLow in the case they must be
* equal to each other, otherwise it will point to optionalMarginHigh.
*/
// 定义各个 BigInt 变量指向 bigints 数组中的相应位置
BigInt *mantissa = &bigints[0]; /* the only initialized bigint */
BigInt *scale = &bigints[1];
BigInt *scaledValue = &bigints[2];
BigInt *scaledMarginLow = &bigints[3];
BigInt *scaledMarginHigh;
BigInt *optionalMarginHigh = &bigints[4];
BigInt *temp1 = &bigints[5];
BigInt *temp2 = &bigints[6];
// 定义常量 log10_2 表示对数10以2为底的值
const npy_float64 log10_2 = 0.30102999566398119521373889472449;
npy_int32 digitExponent, hiBlock;
npy_int32 cutoff_max_Exponent, cutoff_min_Exponent;
npy_uint32 outputDigit; /* current digit being output */
npy_uint32 outputLen;
npy_bool isEven = BigInt_IsEven(mantissa); // 判断 mantissa 是否为偶数
npy_int32 cmp;
// values used to determine how to round
// 用于确定四舍五入方式的值
npy_bool low, high, roundDown;
DEBUG_ASSERT(bufferSize > 0); // 断言确保 bufferSize 大于0
// 如果 mantissa 为零,则结果为零,无需进一步计算
if (BigInt_IsZero(mantissa)) {
*curDigit = '0'; // 当前数字为 '0'
*pOutExponent = 0; // 输出指数为 0
return 1; // 返回结果字符长度为1
}
// 将 mantissa 复制到 scaledValue 中,作为起始计算值
BigInt_Copy(scaledValue, mantissa);
if (hasUnequalMargins) {
/* 如果边距不相等 */
/* 如果指数大于0,表示没有小数部分 */
if (exponent > 0) {
/*
* 1) 将输入值通过扩展乘以尾数和指数来展开。这表示输入值的整数表示方式。
* 2) 应用额外的缩放因子2,以简化后续与边距值的比较。
* 3) 将边距值设置为最低尾数位的缩放因子。
*/
/* scaledValue = 2 * 2 * mantissa*2^exponent */
BigInt_ShiftLeft(scaledValue, exponent + 2);
/* scale = 2 * 2 * 1 */
BigInt_Set_uint32(scale, 4);
/* scaledMarginLow = 2 * 2^(exponent-1) */
BigInt_Pow2(scaledMarginLow, exponent);
/* scaledMarginHigh = 2 * 2 * 2^(exponent-1) */
BigInt_Pow2(optionalMarginHigh, exponent + 1);
}
/* else we have a fractional exponent */
else {
/*
* 为了将尾数数据作为整数进行跟踪,我们将其存储为具有较大缩放的形式。
*/
/* scaledValue = 2 * 2 * mantissa */
BigInt_ShiftLeft(scaledValue, 2);
/* scale = 2 * 2 * 2^(-exponent) */
BigInt_Pow2(scale, -exponent + 2);
/* scaledMarginLow = 2 * 2^(-1) */
BigInt_Set_uint32(scaledMarginLow, 1);
/* scaledMarginHigh = 2 * 2 * 2^(-1) */
BigInt_Set_uint32(optionalMarginHigh, 2);
}
/* 高和低边距不同 */
scaledMarginHigh = optionalMarginHigh;
}
else {
/* 如果边距相等 */
/* 如果指数大于0,表示没有小数部分 */
if (exponent > 0) {
/* scaledValue = 2 * mantissa*2^exponent */
BigInt_ShiftLeft(scaledValue, exponent + 1);
/* scale = 2 * 1 */
BigInt_Set_uint32(scale, 2);
/* scaledMarginLow = 2 * 2^(exponent-1) */
BigInt_Pow2(scaledMarginLow, exponent);
}
/* else we have a fractional exponent */
else {
/*
* 为了将尾数数据作为整数进行跟踪,我们将其存储为具有较大缩放的形式。
*/
/* scaledValue = 2 * mantissa */
BigInt_ShiftLeft(scaledValue, 1);
/* scale = 2 * 2^(-exponent) */
BigInt_Pow2(scale, -exponent + 1);
/* scaledMarginLow = 2 * 2^(-1) */
BigInt_Set_uint32(scaledMarginLow, 1);
}
/* 高和低边距相等 */
scaledMarginHigh = scaledMarginLow;
}
/*
* 根据 Burger 和 Dybvig 的论文优化计算 digitExponent 的估算值,确保正确或者略低一位。
* 参考论文链接:https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.4656
* 我们额外减去 0.69 是为了增加估算失败的频率,这样可以在代码中执行更快速的分支。
* 选择 0.69 的原因是因为 0.69 + log10(2) 比 1 小一个合理的 epsilon,可以考虑到任何浮点数误差。
*
* 我们希望将 digitExponent 设置为 floor(log10(v)) + 1
* v = mantissa*2^exponent
* log2(v) = log2(mantissa) + exponent;
* log10(v) = log2(v) * log10(2)
* floor(log2(v)) = mantissaBit + exponent;
* log10(v) - log10(2) < (mantissaBit + exponent) * log10(2) <= log10(v)
* log10(v) < (mantissaBit + exponent) * log10(2) + log10(2)
* <= log10(v) + log10(2)
* floor(log10(v)) < ceil((mantissaBit + exponent) * log10(2))
* <= floor(log10(v)) + 1
*
* 注意:这个计算假设 npy_float64 是 IEEE-binary64 浮点数。如果情况不是这样,可能需要更新此行代码。
*/
digitExponent = (npy_int32)(
ceil((npy_float64)((npy_int32)mantissaBit + exponent) * log10_2 - 0.69));
/*
* 如果 digitExponent 小于分数截断的最小期望位数,将其调整到合法范围内,以便后续进行四舍五入。
* 注意,虽然 digitExponent 仍然是一个估算值,但这是安全的,因为它只会增加数字。
* 这将修正 digitExponent 至准确值或将其夹到准确值之上。
*/
if (cutoff_max >= 0 && cutoffMode == CutoffMode_FractionLength &&
digitExponent <= -cutoff_max) {
digitExponent = -cutoff_max + 1;
}
/* 将值除以 10^digitExponent。 */
if (digitExponent > 0) {
/* 正指数意味着除法,因此我们需要乘以相应的倍数。 */
BigInt_MultiplyPow10(scale, digitExponent, temp1);
}
else if (digitExponent < 0) {
/*
* 负指数意味着乘法,因此我们需要对 scaledValue、scaledMarginLow 和 scaledMarginHigh 进行乘法运算。
*/
BigInt *temp=temp1, *pow10=temp2;
BigInt_Pow10(pow10, -digitExponent, temp);
BigInt_Multiply(temp, scaledValue, pow10);
BigInt_Copy(scaledValue, temp);
BigInt_Multiply(temp, scaledMarginLow, pow10);
BigInt_Copy(scaledMarginLow, temp);
if (scaledMarginHigh != scaledMarginLow) {
BigInt_Multiply2(scaledMarginHigh, scaledMarginLow);
}
}
/* 如果 (value >= 1),表示我们对 digitExponent 的估计太低了 */
if (BigInt_Compare(scaledValue, scale) >= 0) {
/*
* 指数估计错误。
* 增加指数并且不执行第一个循环迭代所需的预乘操作。
*/
digitExponent = digitExponent + 1;
}
else {
/*
* 指数估计正确。
* 将 scaledValue 和 scaledMarginLow 分别乘以输出基数,为第一个循环迭代做准备。
*/
BigInt_Multiply10(scaledValue);
BigInt_Multiply10(scaledMarginLow);
if (scaledMarginHigh != scaledMarginLow) {
BigInt_Multiply2(scaledMarginHigh, scaledMarginLow);
}
}
/*
* 计算 cutoff_max 指数(要打印的最后一个数字的指数)。默认为输出缓冲区的最大大小。
*/
cutoff_max_Exponent = digitExponent - bufferSize;
if (cutoff_max >= 0) {
npy_int32 desiredCutoffExponent;
if (cutoffMode == CutoffMode_TotalLength) {
desiredCutoffExponent = digitExponent - cutoff_max;
if (desiredCutoffExponent > cutoff_max_Exponent) {
cutoff_max_Exponent = desiredCutoffExponent;
}
}
/* 否则是 CutoffMode_FractionLength。打印小数点后的 cutoff_max 位数字或直到达到缓冲区大小 */
else {
desiredCutoffExponent = -cutoff_max;
if (desiredCutoffExponent > cutoff_max_Exponent) {
cutoff_max_Exponent = desiredCutoffExponent;
}
}
}
/* 同样计算 cutoff_min 指数。 */
cutoff_min_Exponent = digitExponent;
if (cutoff_min >= 0) {
npy_int32 desiredCutoffExponent;
if (cutoffMode == CutoffMode_TotalLength) {
desiredCutoffExponent = digitExponent - cutoff_min;
if (desiredCutoffExponent < cutoff_min_Exponent) {
cutoff_min_Exponent = desiredCutoffExponent;
}
}
else {
desiredCutoffExponent = -cutoff_min;
if (desiredCutoffExponent < cutoff_min_Exponent) {
cutoff_min_Exponent = desiredCutoffExponent;
}
}
}
/* 输出将要打印的第一个数字的指数 */
*pOutExponent = digitExponent - 1;
/*
* 为了调用 BigInt_DivideWithRemainder_MaxQuotient9() 做准备,
* 我们需要扩展我们的值,以使分母的最高块大于或等于8。
* 我们还需要保证在每个循环迭代之后,分子的长度永远不会大于分母的长度。
* 这要求分母的最高块小于或等于 429496729,即可以乘以10而不会溢出到新的块。
*/
DEBUG_ASSERT(scale->length > 0);
// 获取最高块的值
hiBlock = scale->blocks[scale->length - 1];
// 检查最高块是否在有效范围内,若不在则执行以下操作
if (hiBlock < 8 || hiBlock > 429496729) {
npy_uint32 hiBlockLog2, shift;
/*
* 对所有值执行位移,将分母的最高块移动到范围[8, 429496729]内。
* 我们更有可能在 BigInt_DivideWithRemainder_MaxQuotient9() 中使用较高的分母值进行准确的商估算,
* 因此我们将分母移位,使最高位在最高块的第27位。
* 这是安全的,因为 (2^28 - 1) = 268435455 小于 429496729。
* 这意味着所有最高位在第27位的值都在有效范围内。
*/
hiBlockLog2 = LogBase2_32(hiBlock);
DEBUG_ASSERT(hiBlockLog2 < 3 || hiBlockLog2 > 27);
shift = (32 + 27 - hiBlockLog2) % 32;
BigInt_ShiftLeft(scale, shift);
BigInt_ShiftLeft(scaledValue, shift);
BigInt_ShiftLeft(scaledMarginLow, shift);
if (scaledMarginHigh != scaledMarginLow) {
BigInt_Multiply2(scaledMarginHigh, scaledMarginLow);
}
}
if (digitMode == DigitMode_Unique) {
/*
* 对于唯一截断模式,我们将尝试打印直到达到可以唯一区分此值与其邻居的精度级别。
* 如果输出缓冲区空间不足,我们会提前终止。
*/
for (;;) {
BigInt *scaledValueHigh = temp1;
digitExponent = digitExponent - 1;
/* 除以比例以提取数字 */
outputDigit =
BigInt_DivideWithRemainder_MaxQuotient9(scaledValue, scale);
DEBUG_ASSERT(outputDigit < 10);
/* 更新值的高端 */
BigInt_Add(scaledValueHigh, scaledValue, scaledMarginHigh);
/*
* 如果我们距离相邻值足够远(并且我们已经打印了至少请求的最小数字),
* 或者已达到截断数字,则停止循环。
*/
cmp = BigInt_Compare(scaledValue, scaledMarginLow);
low = isEven ? (cmp <= 0) : (cmp < 0);
cmp = BigInt_Compare(scaledValueHigh, scale);
high = isEven ? (cmp >= 0) : (cmp > 0);
if (((low | high) & (digitExponent <= cutoff_min_Exponent)) |
(digitExponent == cutoff_max_Exponent)) {
break;
}
/* 存储输出数字 */
*curDigit = (char)('0' + outputDigit);
++curDigit;
/* 将较大值乘以输出基数 */
BigInt_Multiply10(scaledValue);
BigInt_Multiply10(scaledMarginLow);
if (scaledMarginHigh != scaledMarginLow) {
BigInt_Multiply2(scaledMarginHigh, scaledMarginLow);
}
}
}
else {
/*
* For exact digit mode, we will try to print until we
* have exhausted all precision (i.e. all remaining digits are zeros) or
* until we reach the desired cutoff digit.
*/
// 设置低位和高位标志为假,用于控制舍入
low = NPY_FALSE;
high = NPY_FALSE;
// 无限循环,直到满足退出条件
for (;;) {
// 降低数字指数
digitExponent = digitExponent - 1;
/* divide out the scale to extract the digit */
// 使用 BigInt_DivideWithRemainder_MaxQuotient9 函数从 scaledValue 中除去比例以提取数字
outputDigit = BigInt_DivideWithRemainder_MaxQuotient9(scaledValue, scale);
DEBUG_ASSERT(outputDigit < 10);
// 如果 scaledValue 的长度为零,或者达到最大截断数字指数,则退出循环
if ((scaledValue->length == 0) |
(digitExponent == cutoff_max_Exponent)) {
break;
}
// 存储输出的数字
*curDigit = (char)('0' + outputDigit);
++curDigit;
// 将 scaledValue 乘以 10,准备下一个数字
BigInt_Multiply10(scaledValue);
}
}
/* default to rounding down the final digit if value got too close to 0 */
// 如果低位为真,则默认向下舍入最后一个数字,如果值接近于零
roundDown = low;
/* if it is legal to round up and down */
// 如果可以同时进行向上和向下舍入
if (low == high) {
npy_int32 compare;
/*
* round to the closest digit by comparing value with 0.5. To do this we
* need to convert the inequality to large integer values.
* compare( value, 0.5 )
* compare( scale * value, scale * 0.5 )
* compare( 2 * scale * value, scale )
*/
// 将 scaledValue 乘以 2,执行 BigInt_Multiply2_inplace 操作
BigInt_Multiply2_inplace(scaledValue);
// 比较 scaledValue 和 scale 的大小关系
compare = BigInt_Compare(scaledValue, scale);
// 根据比较结果决定是否向下舍入
roundDown = compare < 0;
/*
* if we are directly in the middle, round towards the even digit (i.e.
* IEEE rounding rules)
*/
// 如果处于中间位置,按照偶数舍入规则处理
if (compare == 0) {
roundDown = (outputDigit & 1) == 0;
}
}
/* print the rounded digit */
// 打印舍入后的数字
if (roundDown) {
*curDigit = (char)('0' + outputDigit);
++curDigit;
}
else {
/* handle rounding up */
// 处理向上舍入的情况
if (outputDigit == 9) {
/* find the first non-nine prior digit */
// 寻找第一个不为九的前一个数字
for (;;) {
/* if we are at the first digit */
// 如果已经到达第一个数字
if (curDigit == pOutBuffer) {
/* output 1 at the next highest exponent */
// 在更高的指数位置输出 1
*curDigit = '1';
++curDigit;
*pOutExponent += 1;
break;
}
--curDigit;
// 如果当前数字不是 '9',则将其加一
if (*curDigit != '9') {
*curDigit += 1;
++curDigit;
break;
}
}
}
else {
/* values in the range [0,8] can perform a simple round up */
// 在范围 [0,8] 内的值可以进行简单的向上舍入
*curDigit = (char)('0' + outputDigit + 1);
++curDigit;
}
}
/* return the number of digits output */
// 返回输出的数字长度
outputLen = (npy_uint32)(curDigit - pOutBuffer);
DEBUG_ASSERT(outputLen <= bufferSize);
return outputLen;
/*
* 结构体 Dragon4_Options 是用于方便传递 Dragon4 选项的结构体。
*
* scientific - 控制是否使用科学计数法
* digit_mode - 决定使用唯一或固定小数输出
* cutoff_mode - 'precision' 是指所有数字,还是小数点后的数字
* precision - 当为负数时,打印足够唯一数字所需的位数;当为正数时,指定最大有效数字数
* sign - 是否始终显示符号
* trim_mode - 如何处理尾随的 0 和 '.'。参见 TrimMode 注释。
* digits_left - 小数点左边的填充字符数。-1 表示不填充
* digits_right - 小数点右边的填充字符数。-1 表示不填充。
* 填充会添加空格,直到小数点两边各有指定数量的字符。应用于 trim_mode 字符移除后。
* 如果 digits_right 是正数且小数点被移除,小数点将被空格字符替换。
* exp_digits - 仅影响科学计数法输出。如果为正数,用 0 填充指数直到达到这么多位数。如果为负数,只使用足够的位数。
*/
typedef struct Dragon4_Options {
npy_bool scientific;
DigitMode digit_mode;
CutoffMode cutoff_mode;
npy_int32 precision;
npy_int32 min_digits;
npy_bool sign;
TrimMode trim_mode;
npy_int32 digits_left;
npy_int32 digits_right;
npy_int32 exp_digits;
} Dragon4_Options;
/*
* 输出正数的定点表示法:ddddd.dddd
* 输出总是以 NUL 结尾,并返回输出长度(不包括 NUL)。
*
* 参数:
* buffer - 输出缓冲区
* bufferSize - 可以打印到缓冲区的最大字符数
* mantissa - 数值的尾数
* exponent - 数值在二进制中的指数
* signbit - 符号位的值。应为 '+', '-' 或 ''
* mantissaBit - 最高设置的尾数位索引
* hasUnequalMargins - 高边界是否是低边界的两倍
*
* 更多参数详见 Dragon4_Options 的描述。
*/
static npy_uint32
/* 定义函数 FormatPositional,用于格式化并生成一个浮点数的字符串表示 */
FormatPositional(char *buffer, npy_uint32 bufferSize, BigInt *mantissa,
npy_int32 exponent, char signbit, npy_uint32 mantissaBit,
npy_bool hasUnequalMargins, DigitMode digit_mode,
CutoffMode cutoff_mode, npy_int32 precision,
npy_int32 min_digits, TrimMode trim_mode,
npy_int32 digits_left, npy_int32 digits_right)
{
/* 声明局部变量 */
npy_int32 printExponent;
npy_int32 numDigits, numWholeDigits=0, has_sign=0;
npy_int32 add_digits;
/* 计算可以存储的最大字符串长度,并初始化位置指针 */
npy_int32 maxPrintLen = (npy_int32)bufferSize - 1, pos = 0;
/* 断言缓冲区大小大于零,确保缓冲区有效 */
DEBUG_ASSERT(bufferSize > 0);
/* 根据数字模式检查精度是否非负 */
if (digit_mode != DigitMode_Unique) {
DEBUG_ASSERT(precision >= 0);
}
/* 如果符号为正,则在缓冲区的开头添加 '+' */
if (signbit == '+' && pos < maxPrintLen) {
buffer[pos++] = '+';
has_sign = 1;
}
/* 如果符号为负,则在缓冲区的开头添加 '-' */
else if (signbit == '-' && pos < maxPrintLen) {
buffer[pos++] = '-';
has_sign = 1;
}
/* 调用 Dragon4 函数生成数字字符串,返回生成的数字字符数量 */
numDigits = Dragon4(mantissa, exponent, mantissaBit, hasUnequalMargins,
digit_mode, cutoff_mode, precision, min_digits,
buffer + has_sign, maxPrintLen - has_sign,
&printExponent);
/* 断言生成的数字字符数量大于零,并且不超过缓冲区大小 */
DEBUG_ASSERT(numDigits > 0);
DEBUG_ASSERT(numDigits <= bufferSize);
/* 如果打印指数大于等于零,表示有整数部分 */
if (printExponent >= 0) {
/* 将整数部分留在缓冲区的开头 */
numWholeDigits = printExponent + 1;
/* 如果生成的数字字符数量小于等于整数部分的数量 */
if (numDigits <= numWholeDigits) {
npy_int32 count = numWholeDigits - numDigits;
pos += numDigits;
/* 避免缓冲区溢出 */
if (pos + count > maxPrintLen) {
count = maxPrintLen - pos;
}
/* 添加末尾的零直到小数点位置 */
numDigits += count;
for (; count > 0; count--) {
buffer[pos++] = '0';
}
}
/* 如果生成的数字字符数量大于整数部分数量 */
else if (numDigits > numWholeDigits) {
npy_int32 maxFractionDigits;
/* 计算小数部分的数量,并限制在缓冲区可容纳的范围内 */
numFractionDigits = numDigits - numWholeDigits;
maxFractionDigits = maxPrintLen - numWholeDigits - 1 - pos;
if (numFractionDigits > maxFractionDigits) {
numFractionDigits = maxFractionDigits;
}
/* 移动小数部分至正确的位置,插入小数点 */
memmove(buffer + pos + numWholeDigits + 1,
buffer + pos + numWholeDigits, numFractionDigits);
pos += numWholeDigits;
buffer[pos] = '.';
numDigits = numWholeDigits + 1 + numFractionDigits;
pos += 1 + numFractionDigits;
}
}
else {
/* 将小数部分移出,以便为前导零腾出空间 */
npy_int32 numFractionZeros = 0;
if (pos + 2 < maxPrintLen) {
npy_int32 maxFractionZeros, digitsStartIdx, maxFractionDigits, i;
maxFractionZeros = maxPrintLen - 2 - pos;
numFractionZeros = -(printExponent + 1);
if (numFractionZeros > maxFractionZeros) {
numFractionZeros = maxFractionZeros;
}
digitsStartIdx = 2 + numFractionZeros;
/*
* 将有效数字向右移动,以便有足够空间放置前导零
*/
numFractionDigits = numDigits;
maxFractionDigits = maxPrintLen - digitsStartIdx - pos;
if (numFractionDigits > maxFractionDigits) {
numFractionDigits = maxFractionDigits;
}
memmove(buffer + pos + digitsStartIdx, buffer + pos,
numFractionDigits);
/* 插入前导零 */
for (i = 2; i < digitsStartIdx; ++i) {
buffer[pos + i] = '0';
}
/* 更新计数 */
numFractionDigits += numFractionZeros;
numDigits = numFractionDigits;
}
/* 添加小数点 */
if (pos + 1 < maxPrintLen) {
buffer[pos+1] = '.';
}
/* 添加初始零 */
if (pos < maxPrintLen) {
buffer[pos] = '0';
numDigits += 1;
}
numWholeDigits = 1;
pos += 2 + numFractionDigits;
}
/* 总是添加小数点,除非是 DprZeros 模式 */
if (trim_mode != TrimMode_DptZeros && numFractionDigits == 0 &&
pos < maxPrintLen) {
buffer[pos++] = '.';
}
add_digits = digit_mode == DigitMode_Unique ? min_digits : precision;
desiredFractionalDigits = add_digits < 0 ? 0 : add_digits;
if (cutoff_mode == CutoffMode_TotalLength) {
desiredFractionalDigits = add_digits - numWholeDigits;
}
if (trim_mode == TrimMode_LeaveOneZero) {
/* 如果没有打印任何小数位,则添加一个尾随的 0 */
if (numFractionDigits == 0 && pos < maxPrintLen) {
buffer[pos++] = '0';
numFractionDigits++;
}
}
else if (trim_mode == TrimMode_None &&
desiredFractionalDigits > numFractionDigits &&
pos < maxPrintLen) {
/* 添加尾随的零,直到达到 add_digits 的长度 */
/* 计算所需的尾随零的数量 */
npy_int32 count = desiredFractionalDigits - numFractionDigits;
if (pos + count > maxPrintLen) {
count = maxPrintLen - pos;
}
numFractionDigits += count;
for ( ; count > 0; count--) {
buffer[pos++] = '0';
}
}
/* 否则,对于 trim_mode Zeros 或 DptZeros,无需再添加任何内容 */
}
/*
* 当进行四舍五入时,可能会产生末尾的零。根据修剪模式决定是否删除这些零。
*/
if (trim_mode != TrimMode_None && numFractionDigits > 0) {
// 循环移除末尾的零直到不是零为止
while (buffer[pos-1] == '0') {
pos--;
numFractionDigits--;
}
// 如果末尾是小数点
if (buffer[pos-1] == '.') {
/* 在 TrimMode_LeaveOneZero 模式下,添加末尾的零 */
if (trim_mode == TrimMode_LeaveOneZero){
buffer[pos++] = '0';
numFractionDigits++;
}
/* 在 TrimMode_DptZeros 模式下,移除末尾的小数点 */
else if (trim_mode == TrimMode_DptZeros) {
pos--;
}
}
}
/* 添加右侧的空白填充 */
if (digits_right >= numFractionDigits) {
npy_int32 count = digits_right - numFractionDigits;
/* 在 TrimMode_DptZeros 模式下,如果小数位数为零,且未到达最大打印长度,则添加一个空格代替小数点 */
if (trim_mode == TrimMode_DptZeros && numFractionDigits == 0
&& pos < maxPrintLen) {
buffer[pos++] = ' ';
}
// 如果要添加的空格数超过了最大打印长度与当前位置的差值,则调整为最大可添加的空格数
if (pos + count > maxPrintLen) {
count = maxPrintLen - pos;
}
// 添加右侧的空白填充
for ( ; count > 0; count--) {
buffer[pos++] = ' ';
}
}
/* 添加左侧的空白填充 */
if (digits_left > numWholeDigits + has_sign) {
npy_int32 shift = digits_left - (numWholeDigits + has_sign);
npy_int32 count = pos;
// 如果要移动的字符数超过了最大打印长度与当前位置的差值,则调整为最大可移动的字符数
if (count + shift > maxPrintLen) {
count = maxPrintLen - shift;
}
// 将 buffer 中的字符向右移动 shift 个位置
if (count > 0) {
memmove(buffer + shift, buffer, count);
}
// 更新当前位置
pos = shift + count;
// 在左侧填充空白
for ( ; shift > 0; shift--) {
buffer[shift - 1] = ' ';
}
}
/* 终止缓冲区 */
DEBUG_ASSERT(pos <= maxPrintLen);
buffer[pos] = '\0';
// 返回有效字符数
return pos;
/*
* Outputs the positive number with scientific notation: d.dddde[sign]ddd
* The output is always NUL terminated and the output length (not including the
* NUL) is returned.
*
* Arguments:
* buffer - buffer to output into
* bufferSize - maximum characters that can be printed to buffer
* mantissa - value significand
* exponent - value exponent in base 2
* signbit - value of the sign position. Should be '+', '-' or ''
* mantissaBit - index of the highest set mantissa bit
* hasUnequalMargins - is the high margin twice as large as the low margin
*
* See Dragon4_Options for description of remaining arguments.
*/
static npy_uint32
FormatScientific (char *buffer, npy_uint32 bufferSize, BigInt *mantissa,
npy_int32 exponent, char signbit, npy_uint32 mantissaBit,
npy_bool hasUnequalMargins, DigitMode digit_mode,
npy_int32 precision, npy_int32 min_digits, TrimMode trim_mode,
npy_int32 digits_left, npy_int32 exp_digits)
{
npy_int32 printExponent;
npy_int32 numDigits;
char *pCurOut;
npy_int32 numFractionDigits;
npy_int32 leftchars;
npy_int32 add_digits;
// 如果不是唯一数字模式,确保精度大于等于零
if (digit_mode != DigitMode_Unique) {
DEBUG_ASSERT(precision >= 0);
}
// 确保缓冲区大小大于零
DEBUG_ASSERT(bufferSize > 0);
pCurOut = buffer;
/* add any whitespace padding to left side */
// 添加任何左侧的空白填充
leftchars = 1 + (signbit == '-' || signbit == '+');
if (digits_left > leftchars) {
int i;
for (i = 0; i < digits_left - leftchars && bufferSize > 1; i++) {
*pCurOut = ' ';
pCurOut++;
--bufferSize;
}
}
// 添加正号或负号
if (signbit == '+' && bufferSize > 1) {
*pCurOut = '+';
pCurOut++;
--bufferSize;
}
else if (signbit == '-' && bufferSize > 1) {
*pCurOut = '-';
pCurOut++;
--bufferSize;
}
// 使用 Dragon4 算法生成科学计数法数字,并获取生成的数字长度
numDigits = Dragon4(mantissa, exponent, mantissaBit, hasUnequalMargins,
digit_mode, CutoffMode_TotalLength,
precision < 0 ? -1 : precision + 1,
min_digits < 0 ? -1 : min_digits + 1,
pCurOut, bufferSize, &printExponent);
// 确保生成的数字长度大于零,并不超过缓冲区大小
DEBUG_ASSERT(numDigits > 0);
DEBUG_ASSERT(numDigits <= bufferSize);
/* keep the whole number as the first digit */
// 将整数部分作为第一个数字保留
if (bufferSize > 1) {
pCurOut += 1;
bufferSize -= 1;
}
/* insert the decimal point prior to the fractional number */
// 在小数部分之前插入小数点
numFractionDigits = numDigits - 1;
if (numFractionDigits > 0 && bufferSize > 1) {
npy_int32 maxFractionDigits = (npy_int32)bufferSize - 2;
if (numFractionDigits > maxFractionDigits) {
numFractionDigits = maxFractionDigits;
}
// 将数字向后移动一个位置,插入小数点
memmove(pCurOut + 1, pCurOut, numFractionDigits);
pCurOut[0] = '.';
pCurOut += (1 + numFractionDigits);
bufferSize -= (1 + numFractionDigits);
}
/* 如果 trim_mode 不是 TrimMode_DptZeros 并且 numFractionDigits 等于 0,并且 bufferSize 大于 1,则添加小数点 */
if (trim_mode != TrimMode_DptZeros && numFractionDigits == 0 &&
bufferSize > 1) {
*pCurOut = '.';
++pCurOut;
--bufferSize;
}
/* 根据 digit_mode 和 precision 确定要添加的数字位数 */
add_digits = digit_mode == DigitMode_Unique ? min_digits : precision;
add_digits = add_digits < 0 ? 0 : add_digits;
/* 如果 trim_mode 是 TrimMode_LeaveOneZero */
if (trim_mode == TrimMode_LeaveOneZero) {
/* 如果没有打印任何小数位,则添加一个 '0' */
if (numFractionDigits == 0 && bufferSize > 1) {
*pCurOut = '0';
++pCurOut;
--bufferSize;
++numFractionDigits;
}
}
/* 如果 trim_mode 是 TrimMode_None */
else if (trim_mode == TrimMode_None) {
/* 添加尾部的零,直到达到 add_digits 指定的长度 */
if (add_digits > (npy_int32)numFractionDigits) {
char *pEnd;
/* 计算需要添加的尾部零的个数 */
npy_int32 numZeros = (add_digits - numFractionDigits);
/* 如果需要添加的零超过了 bufferSize 减去1,则限制为 bufferSize 减去1 */
if (numZeros > (npy_int32)bufferSize - 1) {
numZeros = (npy_int32)bufferSize - 1;
}
/* 在输出缓冲区的尾部添加零 */
for (pEnd = pCurOut + numZeros; pCurOut < pEnd; ++pCurOut) {
*pCurOut = '0';
++numFractionDigits;
}
}
}
/* 如果 trim_mode 是 TrimMode_Zeros 或 TrimMode_DptZeros,则不需要添加更多内容 */
/*
* 当进行四舍五入时,可能会有多余的尾部零。根据 trim 设置进行移除。
*/
if (trim_mode != TrimMode_None && numFractionDigits > 0) {
/* 向前移动 pCurOut,直到遇到非 '0' 字符 */
--pCurOut;
while (*pCurOut == '0') {
--pCurOut;
++bufferSize;
--numFractionDigits;
}
/* 如果 trim_mode 是 TrimMode_LeaveOneZero,并且最后一个字符是小数点,则添加一个 '0' */
if (trim_mode == TrimMode_LeaveOneZero && *pCurOut == '.') {
++pCurOut;
*pCurOut = '0';
--bufferSize;
++numFractionDigits;
}
++pCurOut;
}
/* 将指数打印到本地缓冲区,并复制到输出缓冲区 */
// 如果缓冲区大小大于1,则执行以下操作
if (bufferSize > 1) {
// 定义指数缓冲区和数字数组
char exponentBuffer[7];
npy_int32 digits[5];
npy_int32 i, exp_size, count;
// 如果指数数字大于5,则将其限制为5
if (exp_digits > 5) {
exp_digits = 5;
}
// 如果指数数字小于0,则将其设置为2
if (exp_digits < 0) {
exp_digits = 2;
}
// 设置指数缓冲区的第一个字符为'e'
exponentBuffer[0] = 'e';
// 根据打印指数的正负性设置第二个字符
if (printExponent >= 0) {
exponentBuffer[1] = '+';
}
else {
exponentBuffer[1] = '-';
printExponent = -printExponent;
}
// 调试断言,验证打印指数小于100000
DEBUG_ASSERT(printExponent < 100000);
/* 获取指数的各个数字 */
for (i = 0; i < 5; i++) {
digits[i] = printExponent % 10;
printExponent /= 10;
}
/* 回溯以去除前导零 */
for (i = 5; i > exp_digits && digits[i-1] == 0; i--) {
}
// 计算有效的指数大小
exp_size = i;
/* 将剩余的数字写入临时缓冲区 */
for (i = exp_size; i > 0; i--) {
exponentBuffer[2 + (exp_size-i)] = (char)('0' + digits[i-1]);
}
/* 将指数缓冲区复制到输出中 */
count = exp_size + 2;
// 如果复制长度超过缓冲区大小减一,则截断
if (count > (npy_int32)bufferSize - 1) {
count = (npy_int32)bufferSize - 1;
}
// 将指数缓冲区内容复制到当前输出位置
memcpy(pCurOut, exponentBuffer, count);
// 更新当前输出位置指针
pCurOut += count;
// 更新剩余缓冲区大小
bufferSize -= count;
}
// 调试断言,确保缓冲区大小仍大于0
DEBUG_ASSERT(bufferSize > 0);
// 将字符串结束符写入当前输出位置
pCurOut[0] = '\0';
// 返回填充后的输出长度
return pCurOut - buffer;
/*
* 打印给定宽度的十六进制值。
* 输出的字符串总是以NUL结尾,并返回字符串长度(不包括NUL)。
*/
static npy_uint32
PrintHex(char * buffer, npy_uint32 bufferSize, npy_uint64 value,
npy_uint32 width)
{
const char digits[] = "0123456789abcdef"; // 十六进制数字字符集
char *pCurOut;
DEBUG_ASSERT(bufferSize > 0); // 断言确保缓冲区大小大于0
npy_uint32 maxPrintLen = bufferSize-1;
if (width > maxPrintLen) {
width = maxPrintLen; // 限制打印宽度不超过缓冲区大小
}
pCurOut = buffer;
while (width > 0) {
--width;
npy_uint8 digit = (npy_uint8)((value >> 4ull*(npy_uint64)width) & 0xF); // 获取当前位的十六进制数字
*pCurOut = digits[digit]; // 将当前位的十六进制数字写入输出缓冲区
++pCurOut;
}
*pCurOut = '\0'; // 结束字符串
return pCurOut - buffer; // 返回字符串长度(不包括NUL)
}
/*
* 打印特殊情况下的无穷大和NaN值。
* 输出的字符串总是以NUL结尾,并返回字符串长度(不包括NUL)。
*/
static npy_uint32
PrintInfNan(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
npy_uint32 mantissaHexWidth, char signbit)
{
npy_uint32 maxPrintLen = bufferSize-1;
npy_uint32 pos = 0;
DEBUG_ASSERT(bufferSize > 0); // 断言确保缓冲区大小大于0
/* 检查是否为无穷大 */
if (mantissa == 0) {
npy_uint32 printLen;
/* 只为正负无穷值打印符号(尽管NaN可以有设置符号) */
if (signbit == '+') {
if (pos < maxPrintLen-1) {
buffer[pos++] = '+'; // 如果缓冲区允许,添加正号
}
}
else if (signbit == '-') {
if (pos < maxPrintLen-1) {
buffer[pos++] = '-'; // 如果缓冲区允许,添加负号
}
}
/* 复制字符串并确保缓冲区以NUL结尾 */
printLen = (3 < maxPrintLen - pos) ? 3 : maxPrintLen - pos;
memcpy(buffer + pos, "inf", printLen); // 将字符串 "inf" 复制到缓冲区
buffer[pos + printLen] = '\0'; // 结束字符串
return pos + printLen; // 返回字符串长度(不包括NUL)
}
else {
/* 复制字符串并确保缓冲区以NUL结尾 */
npy_uint32 printLen = (3 < maxPrintLen - pos) ? 3 : maxPrintLen - pos;
memcpy(buffer + pos, "nan", printLen); // 将字符串 "nan" 复制到缓冲区
buffer[pos + printLen] = '\0'; // 结束字符串
/*
* 对于numpy,我们忽略NaN的异常尾数值,但保留此代码以防以后更改我们的想法。
*
* // 追加十六进制值
* if (maxPrintLen > 3) {
* printLen += PrintHex(buffer+3, bufferSize-3, mantissa,
* mantissaHexWidth);
* }
*/
return pos + printLen; // 返回字符串长度(不包括NUL)
}
}
/*
* The functions below format a floating-point numbers stored in particular
* formats, as a decimal string. The output string is always NUL terminated
* and the string length (not including the NUL) is returned.
*
* For 16, 32 and 64 bit floats we assume they are the IEEE 754 type.
* For 128 bit floats we account for different definitions.
*
* Arguments are:
* buffer - buffer to output into
* bufferSize - maximum characters that can be printed to buffer
* value - value to print
* opt - Dragon4 options, see above
*/
/*
* Helper function that takes Dragon4 parameters and options and
* calls Dragon4.
*/
static npy_uint32
Format_floatbits(char *buffer, npy_uint32 bufferSize, BigInt *mantissa,
npy_int32 exponent, char signbit, npy_uint32 mantissaBit,
npy_bool hasUnequalMargins, Dragon4_Options *opt)
{
/* format the value */
if (opt->scientific) {
// 调用科学计数法格式化函数 FormatScientific 来格式化浮点数值
return FormatScientific(buffer, bufferSize, mantissa, exponent,
signbit, mantissaBit, hasUnequalMargins,
opt->digit_mode, opt->precision,
opt->min_digits, opt->trim_mode,
opt->digits_left, opt->exp_digits);
}
else {
// 调用定点表示格式化函数 FormatPositional 来格式化浮点数值
return FormatPositional(buffer, bufferSize, mantissa, exponent,
signbit, mantissaBit, hasUnequalMargins,
opt->digit_mode, opt->cutoff_mode,
opt->precision, opt->min_digits, opt->trim_mode,
opt->digits_left, opt->digits_right);
}
}
/*
* IEEE binary16 floating-point format
*
* sign: 1 bit
* exponent: 5 bits
* mantissa: 10 bits
*/
static npy_uint32
Dragon4_PrintFloat_IEEE_binary16(
Dragon4_Scratch *scratch, npy_half *value, Dragon4_Options *opt)
{
char *buffer = scratch->repr; // 将输出缓冲区指向 scratch 结构体中的 repr 字段
const npy_uint32 bufferSize = sizeof(scratch->repr); // 获取输出缓冲区的最大容量
BigInt *bigints = scratch->bigints; // 获取 scratch 结构体中的大整数数组
npy_uint16 val = *value; // 获取传入的 16 位浮点数值
npy_uint32 floatExponent, floatMantissa, floatSign;
npy_uint32 mantissa;
npy_int32 exponent;
npy_uint32 mantissaBit;
npy_bool hasUnequalMargins;
char signbit = '\0';
/* deconstruct the floating point value */
floatMantissa = val & bitmask_u32(10); // 从浮点数值中获取尾数部分
floatExponent = (val >> 10) & bitmask_u32(5); // 从浮点数值中获取指数部分
floatSign = val >> 15; // 从浮点数值中获取符号位
/* output the sign */
if (floatSign != 0) {
signbit = '-'; // 如果符号位为1,则浮点数为负数
}
else if (opt->sign) {
signbit = '+'; // 如果符号位为0且选项中需要显示符号,则为正数
}
/* if this is a special value */
if (floatExponent == bitmask_u32(5)) {
// 如果指数部分全为1,则该浮点数为特殊值(如无穷大或 NaN),调用 PrintInfNan 来处理
return PrintInfNan(buffer, bufferSize, floatMantissa, 3, signbit);
}
/* else this is a number */
/* factor the value into its parts */
}
if (floatExponent != 0) {
/*
* 如果指数部分不为零,则为规格化浮点数
* 浮点数的计算公式为:
* value = (1 + mantissa/2^10) * 2 ^ (exponent-15)
* 我们通过将指数部分提取出一个2^10,将整数公式转换为:
* value = (1 + mantissa/2^10) * 2^10 * 2 ^ (exponent-15-10)
* value = (2^10 + mantissa) * 2 ^ (exponent-15-10)
* 因为尾数前面有一个隐含的1,所以我们有10位精度。
* m = (2^10 + mantissa)
* e = (exponent-15-10)
*/
mantissa = (1UL << 10) | floatMantissa;
exponent = floatExponent - 15 - 10;
mantissaBit = 10;
hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0);
}
else {
/*
* 如果指数部分为零,则为非规格化浮点数
* 浮点数的计算公式为:
* value = (mantissa/2^10) * 2 ^ (1-15)
* 我们通过将指数部分提取出一个2^23,将整数公式转换为:
* value = (mantissa/2^10) * 2^10 * 2 ^ (1-15-10)
* value = mantissa * 2 ^ (1-15-10)
* 我们有最多10位精度。
* m = (mantissa)
* e = (1-15-10)
*/
mantissa = floatMantissa;
exponent = 1 - 15 - 10;
mantissaBit = LogBase2_32(mantissa);
hasUnequalMargins = NPY_FALSE;
}
// 将尾数转换为大整数形式,并存储在bigints数组的第一个位置
BigInt_Set_uint32(&bigints[0], mantissa);
// 调用Format_floatbits函数,将浮点数各部分以字符串形式格式化到buffer中
return Format_floatbits(buffer, bufferSize, bigints, exponent,
signbit, mantissaBit, hasUnequalMargins, opt);
/*
* IEEE binary32 floating-point format
*
* sign: 1 bit
* exponent: 8 bits
* mantissa: 23 bits
*/
static npy_uint32
Dragon4_PrintFloat_IEEE_binary32(
Dragon4_Scratch *scratch, npy_float32 *value,
Dragon4_Options *opt)
{
char *buffer = scratch->repr; // 将 scratch 结构中的 repr 字段赋给 buffer,用于存储结果字符串
const npy_uint32 bufferSize = sizeof(scratch->repr); // 计算 repr 字段的字节大小并赋给 bufferSize
BigInt *bigints = scratch->bigints; // 将 scratch 结构中的 bigints 字段赋给 bigints,用于存储大整数对象数组
union
{
npy_float32 floatingPoint;
npy_uint32 integer;
} floatUnion; // 定义联合体 floatUnion,用于将浮点数和整数视为同一内存空间
npy_uint32 floatExponent, floatMantissa, floatSign; // 定义浮点数的指数、尾数和符号部分的变量
npy_uint32 mantissa; // 定义用于存储尾数的变量
npy_int32 exponent; // 定义用于存储指数的变量
npy_uint32 mantissaBit; // 定义用于存储尾数位数的变量
npy_bool hasUnequalMargins; // 定义用于标识是否具有不相等边界的布尔变量
char signbit = '\0'; // 定义符号位的字符变量,默认为空字符
/* deconstruct the floating point value */
floatUnion.floatingPoint = *value; // 将传入的浮点数值解构到 floatUnion 中
floatMantissa = floatUnion.integer & bitmask_u32(23); // 获取浮点数的尾数部分
floatExponent = (floatUnion.integer >> 23) & bitmask_u32(8); // 获取浮点数的指数部分
floatSign = floatUnion.integer >> 31; // 获取浮点数的符号部分
/* output the sign */
if (floatSign != 0) {
signbit = '-'; // 如果浮点数为负数,则符号位为负号
}
else if (opt->sign) {
signbit = '+'; // 如果浮点数为正数且 opt 参数要求显示符号,则符号位为正号
}
/* if this is a special value */
if (floatExponent == bitmask_u32(8)) {
return PrintInfNan(buffer, bufferSize, floatMantissa, 6, signbit); // 如果浮点数是特殊值(如无穷大或NaN),则调用打印函数并返回
}
/* else this is a number */
/* factor the value into its parts */
if (floatExponent != 0) {
/*
* normalized
* The floating point equation is:
* value = (1 + mantissa/2^23) * 2 ^ (exponent-127)
* We convert the integer equation by factoring a 2^23 out of the
* exponent
* value = (1 + mantissa/2^23) * 2^23 * 2 ^ (exponent-127-23)
* value = (2^23 + mantissa) * 2 ^ (exponent-127-23)
* Because of the implied 1 in front of the mantissa we have 24 bits of
* precision.
* m = (2^23 + mantissa)
* e = (exponent-127-23)
*/
mantissa = (1UL << 23) | floatMantissa; // 计算规格化数的尾数部分
exponent = floatExponent - 127 - 23; // 计算规格化数的指数部分
mantissaBit = 23; // 尾数位数为 23
hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0); // 判断是否具有不相等的边界
}
else {
/*
* denormalized
* The floating point equation is:
* value = (mantissa/2^23) * 2 ^ (1-127)
* We convert the integer equation by factoring a 2^23 out of the
* exponent
* value = (mantissa/2^23) * 2^23 * 2 ^ (1-127-23)
* value = mantissa * 2 ^ (1-127-23)
* We have up to 23 bits of precision.
* m = (mantissa)
* e = (1-127-23)
*/
mantissa = floatMantissa; // 计算非规格化数的尾数部分
exponent = 1 - 127 - 23; // 计算非规格化数的指数部分
mantissaBit = LogBase2_32(mantissa); // 计算非规格化数的尾数位数
hasUnequalMargins = NPY_FALSE; // 非规格化数不存在不相等的边界
}
BigInt_Set_uint32(&bigints[0], mantissa); // 将尾数存入大整数对象数组中的第一个位置
return Format_floatbits(buffer, bufferSize, bigints, exponent,
signbit, mantissaBit, hasUnequalMargins, opt); // 调用格式化函数,生成浮点数的字符串表示并返回
}
/*
* IEEE binary64 floating-point format
*
* sign: 1 bit // 符号位:1位
* exponent: 11 bits // 指数部分:11位
* mantissa: 52 bits // 尾数部分:52位
*/
static npy_uint32
Dragon4_PrintFloat_IEEE_binary64(
Dragon4_Scratch *scratch, npy_float64 *value, Dragon4_Options *opt)
{
char *buffer = scratch->repr; // 输出缓冲区
const npy_uint32 bufferSize = sizeof(scratch->repr); // 缓冲区大小
BigInt *bigints = scratch->bigints; // 大整数数组引用
union
{
npy_float64 floatingPoint;
npy_uint64 integer;
} floatUnion;
npy_uint32 floatExponent, floatSign;
npy_uint64 floatMantissa;
npy_uint64 mantissa;
npy_int32 exponent;
npy_uint32 mantissaBit;
npy_bool hasUnequalMargins;
char signbit = '\0';
/* 分解浮点数值 */
floatUnion.floatingPoint = *value; // 将浮点数值存入联合体
floatMantissa = floatUnion.integer & bitmask_u64(52); // 提取尾数部分
floatExponent = (floatUnion.integer >> 52) & bitmask_u32(11); // 提取指数部分
floatSign = floatUnion.integer >> 63; // 提取符号位
/* 输出符号位 */
if (floatSign != 0) {
signbit = '-';
}
else if (opt->sign) {
signbit = '+';
}
/* 如果是特殊值 */
if (floatExponent == bitmask_u32(11)) {
return PrintInfNan(buffer, bufferSize, floatMantissa, 13, signbit); // 输出无穷大或NaN
}
/* 否则是一个数字 */
/* 将值分解为其部分 */
if (floatExponent != 0) {
/*
* 正常值
* 浮点数方程为:
* value = (1 + mantissa/2^52) * 2 ^ (exponent-1023)
* 我们通过将2^52从指数中分解出来,将整数方程转换为:
* value = (1 + mantissa/2^52) * 2^52 * 2 ^ (exponent-1023-52)
* value = (2^52 + mantissa) * 2 ^ (exponent-1023-52)
* 因为尾数前面隐含了1,我们有53位精度。
* m = (2^52 + mantissa)
* e = (exponent-1023+1-53)
*/
mantissa = (1ull << 52) | floatMantissa; // 计算正常情况下的尾数
exponent = floatExponent - 1023 - 52; // 计算正常情况下的指数
mantissaBit = 52; // 尾数的位数
hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0); // 检查是否存在不等的边界
}
else {
/*
* 亚正常值
* 浮点数方程为:
* value = (mantissa/2^52) * 2 ^ (1-1023)
* 我们通过将2^52从指数中分解出来,将整数方程转换为:
* value = (mantissa/2^52) * 2^52 * 2 ^ (1-1023-52)
* value = mantissa * 2 ^ (1-1023-52)
* 我们有高达52位的精度。
* m = (mantissa)
* e = (1-1023-52)
*/
mantissa = floatMantissa; // 计算亚正常情况下的尾数
exponent = 1 - 1023 - 52; // 计算亚正常情况下的指数
mantissaBit = LogBase2_64(mantissa); // 尾数的位数
hasUnequalMargins = NPY_FALSE; // 检查是否存在不等的边界
}
BigInt_Set_uint64(&bigints[0], mantissa); // 设置大整数数组中的尾数值
return Format_floatbits(buffer, bufferSize, bigints, exponent,
signbit, mantissaBit, hasUnequalMargins, opt); // 格式化输出浮点数位
}
/*
* 由于不同系统可能有不同类型的 long double,并且可能没有用于传递值的 128 字节格式,
* 因此在这里我们创建自己的 128 位存储类型以方便操作。
*/
typedef struct FloatVal128 {
npy_uint64 hi, lo; // 高位和低位分别存储 64 位无符号整数
} FloatVal128;
#if defined(HAVE_LDOUBLE_INTEL_EXTENDED_10_BYTES_LE) || \
defined(HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE) || \
defined(HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE) || \
defined(HAVE_LDOUBLE_MOTOROLA_EXTENDED_12_BYTES_BE)
/*
* Intel 的 80 位 IEEE 扩展精度浮点格式
*
* 使用此格式的 "long double" 存储为 96 或 128 位,但相当于带有高位零填充的 80 位类型。
* Dragon4_PrintFloat_Intel_extended 函数期望用户使用 128 位的 FloatVal128 传入值,
* 以支持 80、96 或 128 位存储格式,并且是端序无关的。
*
* sign: 1 bit, second u64
* exponent: 15 bits, second u64
* intbit 1 bit, first u64
* mantissa: 63 bits, first u64
*/
static npy_uint32
Dragon4_PrintFloat_Intel_extended(
Dragon4_Scratch *scratch, FloatVal128 value, Dragon4_Options *opt)
{
char *buffer = scratch->repr; // 字符串缓冲区
const npy_uint32 bufferSize = sizeof(scratch->repr); // 缓冲区大小
BigInt *bigints = scratch->bigints; // 大整数数组
npy_uint32 floatExponent, floatSign; // 浮点数的指数和符号
npy_uint64 floatMantissa; // 浮点数的尾数
npy_uint64 mantissa; // 尾数
npy_int32 exponent; // 指数
npy_uint32 mantissaBit; // 尾数位
npy_bool hasUnequalMargins; // 不等间距标志
char signbit = '\0'; // 符号位初始化为空字符
/* 拆解浮点数值(忽略 intbit) */
floatMantissa = value.lo & bitmask_u64(63); // 提取低位 63 位作为浮点数的尾数
floatExponent = value.hi & bitmask_u32(15); // 提取高位 15 位作为浮点数的指数
floatSign = (value.hi >> 15) & 0x1; // 提取符号位,右移 15 位并掩码出最低位
/* 输出符号位 */
if (floatSign != 0) {
signbit = '-'; // 若符号位不为 0,则设为负号
}
else if (opt->sign) {
signbit = '+'; // 否则如果选项中要求显示符号,则设为正号
}
/* 如果这是一个特殊值 */
if (floatExponent == bitmask_u32(15)) {
/*
* 注意:技术上还有其他特殊的扩展值,如果 intbit 为 0,例如伪无穷大、伪 NaN、Quiet NaN。
* 我们忽略所有这些,因为它们在现代处理器上不会生成。我们将 Quiet NaN 视为普通 NaN。
*/
return PrintInfNan(buffer, bufferSize, floatMantissa, 16, signbit); // 打印无穷大或 NaN
}
/* 否则,这是一个数字 */
/* 分解值为其部分 */
// (接下来的代码行未包含在示例中,因此不在注释范围内)
if (floatExponent != 0) {
/*
* normal
* 浮点数的计算公式为:
* value = (1 + mantissa/2^63) * 2 ^ (exponent-16383)
* 我们通过将指数中的 2^63 提取出来,转换为整数计算:
* value = (1 + mantissa/2^63) * 2^63 * 2 ^ (exponent-16383-63)
* value = (2^63 + mantissa) * 2 ^ (exponent-16383-63)
* 因为尾数前有一个隐含的 1,所以我们有64位的精度。
* m = (2^63 + mantissa)
* e = (exponent-16383+1-64)
*/
mantissa = (1ull << 63) | floatMantissa;
exponent = floatExponent - 16383 - 63;
mantissaBit = 63;
hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0);
}
else {
/*
* subnormal
* 浮点数的计算公式为:
* value = (mantissa/2^63) * 2 ^ (1-16383)
* 我们通过将指数中的 2^63 提取出来,转换为整数计算:
* value = (mantissa/2^63) * 2^52 * 2 ^ (1-16383-63)
* value = mantissa * 2 ^ (1-16383-63)
* 我们有最多63位的精度。
* m = (mantissa)
* e = (1-16383-63)
*/
mantissa = floatMantissa;
exponent = 1 - 16383 - 63;
mantissaBit = LogBase2_64(mantissa); // 计算64位整数的对数
hasUnequalMargins = NPY_FALSE; // 设定为假
}
BigInt_Set_uint64(&bigints[0], mantissa); // 设置大整数的64位无符号整数
return Format_floatbits(buffer, bufferSize, bigints, exponent,
signbit, mantissaBit, hasUnequalMargins, opt); // 格式化浮点位,并返回格式化后的字符串
#ifdef NPY_FLOAT128
typedef union FloatUnion128
{
npy_float128 floatingPoint; // 定义一个联合体,支持 npy_float128 类型的浮点数
struct {
npy_uint64 a; // 联合体中的整数部分 a,占用 64 位
npy_uint64 b; // 联合体中的整数部分 b,占用 64 位
} integer;
} FloatUnion128;
#ifdef HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE
/* Intel's 80-bit IEEE extended precision format, 128-bit storage */
static npy_uint32
Dragon4_PrintFloat_Intel_extended128(
Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt)
{
FloatVal128 val128; // 定义一个 FloatVal128 结构体变量
FloatUnion128 buf128; // 定义一个 FloatUnion128 联合体变量,用于存储 npy_float128 类型的浮点数的整数部分
buf128.floatingPoint = *value; // 将传入的 npy_float128 浮点数值存入联合体变量 buf128 中
/* Intel is little-endian */
val128.lo = buf128.integer.a; // 将 buf128 的整数部分 a 赋值给 val128 的低位
val128.hi = buf128.integer.b; // 将 buf128 的整数部分 b 赋值给 val128 的高位
// 调用 Dragon4_PrintFloat_Intel_extended 函数处理 Intel 扩展格式的浮点数输出
return Dragon4_PrintFloat_Intel_extended(scratch, val128, opt);
}
return Dragon4_PrintFloat_Intel_extended(scratch, val128, opt);
/*
* IEEE binary128 floating-point format
*
* sign: 1 bit
* exponent: 15 bits
* mantissa: 112 bits
*
* Currently binary128 format exists on only a few CPUs, such as on the POWER9
* arch or aarch64. Because of this, this code has not been extensively tested.
* I am not sure if the arch also supports uint128, and C does not seem to
* support int128 literals. So we use uint64 to do manipulation.
*/
static npy_uint32
Dragon4_PrintFloat_IEEE_binary128(
Dragon4_Scratch *scratch, FloatVal128 val128, Dragon4_Options *opt)
{
char *buffer = scratch->repr; // 缓冲区指针,用于存储浮点数的字符串表示
const npy_uint32 bufferSize = sizeof(scratch->repr); // 缓冲区的大小
BigInt *bigints = scratch->bigints; // 大整数数组的指针
npy_uint32 floatExponent, floatSign; // 浮点数的指数和符号位
npy_uint64 mantissa_hi, mantissa_lo; // 浮点数的高位和低位有效位
npy_int32 exponent; // 浮点数的指数部分
npy_uint32 mantissaBit; // 浮点数有效位数
npy_bool hasUnequalMargins; // 浮点数是否有不相等的边界
char signbit = '\0'; // 符号位字符,默认为空字符
mantissa_hi = val128.hi & bitmask_u64(48); // 获取浮点数高 64 位中的有效位
mantissa_lo = val128.lo; // 获取浮点数低 64 位中的有效位
floatExponent = (val128.hi >> 48) & bitmask_u32(15); // 获取浮点数的指数部分
floatSign = val128.hi >> 63; // 获取浮点数的符号位
/* output the sign */
if (floatSign != 0) {
signbit = '-'; // 如果符号位为1,表示负数
}
else if (opt->sign) {
signbit = '+'; // 如果符号位为0,且选项中需要显示符号,则为正数
}
/* if this is a special value */
if (floatExponent == bitmask_u32(15)) {
npy_uint64 mantissa_zero = mantissa_hi == 0 && mantissa_lo == 0;
return PrintInfNan(buffer, bufferSize, !mantissa_zero, 16, signbit);
// 如果浮点数是特殊值(如无穷大或NaN),则调用打印函数并返回
}
/* else this is a number */
/* factor the value into its parts */
if (floatExponent != 0) {
/*
* normal
* The floating point equation is:
* value = (1 + mantissa/2^112) * 2 ^ (exponent-16383)
* We convert the integer equation by factoring a 2^112 out of the
* exponent
* value = (1 + mantissa/2^112) * 2^112 * 2 ^ (exponent-16383-112)
* value = (2^112 + mantissa) * 2 ^ (exponent-16383-112)
* Because of the implied 1 in front of the mantissa we have 112 bits of
* precision.
* m = (2^112 + mantissa)
* e = (exponent-16383+1-112)
*
* Adding 2^112 to the mantissa is the same as adding 2^48 to the hi
* 64 bit part.
*/
mantissa_hi = (1ull << 48) | mantissa_hi; // 将2^48加到高64位中,构造浮点数的有效位
/* mantissa_lo is unchanged */
exponent = floatExponent - 16383 - 112; // 计算浮点数的指数部分
mantissaBit = 112; // 浮点数的有效位数为112位
hasUnequalMargins = (floatExponent != 1) && (mantissa_hi == 0 &&
mantissa_lo == 0); // 判断是否有不相等的边界
}
else {
/*
* subnormal
* 浮点数为次正规化情况
* 浮点数计算公式为:
* value = (mantissa/2^112) * 2 ^ (1-16383)
* 我们通过将指数中的 2^112 提取出来,转换为整数计算公式:
* value = (mantissa/2^112) * 2^112 * 2 ^ (1-16383-112)
* value = mantissa * 2 ^ (1-16383-112)
* 我们有高达112位的精度。
* m = (mantissa)
* e = (1-16383-112)
*/
// 计算指数
exponent = 1 - 16383 - 112;
// 计算mantissa的位数
hasUnequalMargins = NPY_FALSE;
}
// 设置BigInt结构体,存
/*
* IBM extended precision 128-bit floating-point format, aka IBM double-double
*
* IBM的双倍精度类型是一对IEEE二进制64位值,将它们相加得到总值。指数排列使得较低的双精度大约比高位双精度小2^52倍,最接近的float64值就是简单地使用上位双精度,此时对成对的值视为“规范化”(不要与“正常”和“次正常”的二进制64位值混淆)。我们假设这些值是规范化的。你可以通过构造非规范化值来看到glibc在ppc上的printf会产生奇怪的行为:
*
* >>> from numpy._core._multiarray_tests import format_float_OSprintf_g
* >>> x = np.array([0.3,0.3], dtype='f8').view('f16')[0]
* >>> format_float_OSprintf_g(x, 2)
* 0.30
* >>> format_float_OSprintf_g(2*x, 2)
* 1.20
*
* 如果我们不假设规范化,x应该打印为0.6。
*
* 对于规范化值,gcc假设总的尾数不超过106位(53+53),因此当左移其指数导致第二个双精度超过106位时,我们可以丢弃一些位数,这种情况有时会发生。(对此曾有过争论,参见 https://gcc.gnu.org/bugzilla/show_bug.cgi?format=multiple&id=70117, https://sourceware.org/bugzilla/show_bug.cgi?id=22752 )
*
* 注意:此函数适用于IBM双倍精度,它是一对IEEE二进制64位浮点数,如在ppc64系统上。这*不是*十六进制的IBM双倍精度类型,后者是一对IBM十六进制64位浮点数。
*
* 参见:
* https://gcc.gnu.org/wiki/Ieee128PowerPCA
* https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.genprogc/128bit_long_double_floating-point_datatype.htm
*/
static npy_uint32
Dragon4_PrintFloat_IBM_double_double(
Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt)
{
// 获取 scratch 结构体中的字符串缓冲区指针
char *buffer = scratch->repr;
// 获取字符串缓冲区大小
const npy_uint32 bufferSize = sizeof(scratch->repr);
// 获取 scratch 结构体中的大整数数组指针
BigInt *bigints = scratch->bigints;
// 定义用于存储浮点数值的结构体和联合体
FloatVal128 val128;
FloatUnion128 buf128;
// 定义用于存储浮点数的指数和尾数的变量
npy_uint32 floatExponent1, floatExponent2;
npy_uint64 floatMantissa1, floatMantissa2;
npy_uint32 floatSign1, floatSign2;
// 定义用于存储转换后的尾数和指数
npy_uint64 mantissa1, mantissa2;
npy_int32 exponent1, exponent2;
int shift;
npy_uint32 mantissaBit;
npy_bool hasUnequalMargins;
char signbit = '\0';
/* The high part always comes before the low part, regardless of the
* endianness of the system. */
// 将浮点数值分解为高位和低位,不受系统字节顺序影响
buf128.floatingPoint = *value;
val128.hi = buf128.integer.a;
val128.lo = buf128.integer.b;
/* deconstruct the floating point values */
// 解析浮点数值,获取尾数、指数和符号位
floatMantissa1 = val128.hi & bitmask_u64(52);
floatExponent1 = (val128.hi >> 52) & bitmask_u32(11);
floatSign1 = (val128.hi >> 63) != 0;
floatMantissa2 = val128.lo & bitmask_u64(52);
floatExponent2 = (val128.lo >> 52) & bitmask_u32(11);
floatSign2 = (val128.lo >> 63) != 0;
/* output the sign using 1st float's sign */
// 根据第一个浮点数的符号位确定输出的符号
if (floatSign1) {
signbit = '-';
}
else if (opt->sign) {
signbit = '+';
}
/* we only need to look at the first float for inf/nan */
// 如果第一个浮点数的指数字段全为1,则表示它是特殊值(无穷大或NaN)
if (floatExponent1 == bitmask_u32(11)) {
// 调用打印无穷大或NaN的函数,并返回结果
return PrintInfNan(buffer, bufferSize, floatMantissa1, 13, signbit);
}
/* else this is a number */
/* Factor the 1st value into its parts, see binary64 for comments. */
// 将第一个浮点数值分解为多个部分,参考binary64的注释
if (floatExponent1 == 0) {
/*
* If the first number is a subnormal value, the 2nd has to be 0 for
* the float128 to be normalized, so we can ignore it. In this case
* the float128 only has the precision of a single binary64 value.
*/
// 如果第一个浮点数是次正规值(subnormal),则第二个浮点数的尾数必须为0,
// 以使得float128规格化,此时float128的精度仅等同于单个binary64值
mantissa1 = floatMantissa1;
exponent1 = 1 - 1023 - 52;
mantissaBit = LogBase2_64(mantissa1);
hasUnequalMargins = NPY_FALSE;
// 将尾数存入大整数结构体的第一个元素
BigInt_Set_uint64(&bigints[0], mantissa1);
}
}
// 调用格式化浮点数位的函数,并返回结果
return Format_floatbits(buffer, bufferSize, bigints, exponent1,
signbit, mantissaBit, hasUnequalMargins, opt);
}
#endif /* HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE | HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE */
#endif /* NPY_FLOAT128 */
/*
* Here we define two Dragon4 entry functions for each type. One of them
* accepts the args in a Dragon4_Options struct for convenience, the
* other enumerates only the necessary parameters.
*
* Use a very large string buffer in case anyone tries to output a large number.
* 16384 should be enough to exactly print the integer part of any float128,
* which goes up to about 10^4932. The Dragon4_scratch struct provides a string
* buffer of this size.
*/
// 定义两个Dragon4入口函数,一个使用Dragon4_Options结构体中的参数,另一个列举必要的参数
#define make_dragon4_typefuncs_inner(Type, npy_type, format) \
\
PyObject *\
PyObject *Dragon4_Positional_##Type##_opt(npy_type *val, Dragon4_Options *opt)\
{\
PyObject *ret;\
// 获取 Dragon4_Scratch 结构的指针,用于 Dragon4 算法的临时存储
Dragon4_Scratch *scratch = get_dragon4_bigint_scratch();\
// 如果获取 Dragon4_Scratch 失败,则返回空指针
if (scratch == NULL) {\
return NULL;\
}\
// 调用 Dragon4_PrintFloat_##format 函数打印浮点数到字符串,若出错则释放 scratch 并返回空指针
if (Dragon4_PrintFloat_##format(scratch, val, opt) < 0) {\
free_dragon4_bigint_scratch(scratch);\
return NULL;\
}\
// 从 scratch->repr 创建 Python Unicode 对象
ret = PyUnicode_FromString(scratch->repr);\
// 释放 Dragon4_Scratch 结构所占用的内存
free_dragon4_bigint_scratch(scratch);\
// 返回 Python Unicode 对象
return ret;\
}\
\
PyObject *\
Dragon4_Positional_##Type(npy_type *val, DigitMode digit_mode,\
CutoffMode cutoff_mode, int precision, int min_digits, \
int sign, TrimMode trim, int pad_left, int pad_right)\
{\
Dragon4_Options opt;\
\
// 初始化 Dragon4_Options 结构体的各个成员变量
opt.scientific = 0;\
opt.digit_mode = digit_mode;\
opt.cutoff_mode = cutoff_mode;\
opt.precision = precision;\
opt.min_digits = min_digits;\
opt.sign = sign;\
opt.trim_mode = trim;\
opt.digits_left = pad_left;\
opt.digits_right = pad_right;\
opt.exp_digits = -1;\
\
// 调用 Dragon4_Positional_##Type##_opt 函数,返回其结果
return Dragon4_Positional_##Type##_opt(val, &opt);\
}\
\
PyObject *\
Dragon4_Scientific_##Type##_opt(npy_type *val, Dragon4_Options *opt)\
{\
PyObject *ret;\
// 获取 Dragon4_Scratch 结构的指针,用于 Dragon4 算法的临时存储
Dragon4_Scratch *scratch = get_dragon4_bigint_scratch();\
// 如果获取 Dragon4_Scratch 失败,则返回空指针
if (scratch == NULL) {\
return NULL;\
}\
// 调用 Dragon4_PrintFloat_##format 函数打印浮点数到字符串,若出错则释放 scratch 并返回空指针
if (Dragon4_PrintFloat_##format(scratch, val, opt) < 0) {\
free_dragon4_bigint_scratch(scratch);\
return NULL;\
}\
// 从 scratch->repr 创建 Python Unicode 对象
ret = PyUnicode_FromString(scratch->repr);\
// 释放 Dragon4_Scratch 结构所占用的内存
free_dragon4_bigint_scratch(scratch);\
// 返回 Python Unicode 对象
return ret;\
}\
\
PyObject *\
Dragon4_Scientific_##Type(npy_type *val, DigitMode digit_mode, int precision,\
int min_digits, int sign, TrimMode trim, int pad_left, \
int exp_digits)\
{\
Dragon4_Options opt;\
\
// 初始化 Dragon4_Options 结构体的各个成员变量
opt.scientific = 1;\
opt.digit_mode = digit_mode;\
opt.cutoff_mode = CutoffMode_TotalLength;\
opt.precision = precision;\
opt.min_digits = min_digits;\
opt.sign = sign;\
opt.trim_mode = trim;\
opt.digits_left = pad_left;\
opt.digits_right = -1;\
opt.exp_digits = exp_digits;\
\
// 调用 Dragon4_Scientific_##Type##_opt 函数,返回其结果
return Dragon4_Scientific_##Type##_opt(val, &opt);\
}
# 设置输出选项的精度
opt.precision = precision;
# 设置输出选项的最小数字位数
opt.min_digits = min_digits;
# 设置输出选项的符号显示
opt.sign = sign;
# 设置输出选项的修剪模式
opt.trim_mode = trim;
# 设置输出选项的左侧填充位数
opt.digits_left = pad_left;
# 设置输出选项的右侧填充位数
opt.digits_right = pad_right;
# 设置输出选项的指数位数为默认值 -1
opt.exp_digits = -1;
# 检查 obj 是否是半精度浮点数标量
if (PyArray_IsScalar(obj, Half)) {
# 获取半精度浮点数的值 x
npy_half x = PyArrayScalar_VAL(obj, Half);
# 调用 Dragon4 算法计算半精度浮点数的字符串表示,并返回结果
return Dragon4_Positional_Half_opt(&x, &opt);
}
# 检查 obj 是否是单精度浮点数标量
else if (PyArray_IsScalar(obj, Float)) {
# 获取单精度浮点数的值 x
npy_float x = PyArrayScalar_VAL(obj, Float);
# 调用 Dragon4 算法计算单精度浮点数的字符串表示,并返回结果
return Dragon4_Positional_Float_opt(&x, &opt);
}
# 检查 obj 是否是双精度浮点数标量
else if (PyArray_IsScalar(obj, Double)) {
# 获取双精度浮点数的值 x
npy_double x = PyArrayScalar_VAL(obj, Double);
# 调用 Dragon4 算法计算双精度浮点数的字符串表示,并返回结果
return Dragon4_Positional_Double_opt(&x, &opt);
}
# 检查 obj 是否是长双精度浮点数标量
else if (PyArray_IsScalar(obj, LongDouble)) {
# 获取长双精度浮点数的值 x
npy_longdouble x = PyArrayScalar_VAL(obj, LongDouble);
# 调用 Dragon4 算法计算长双精度浮点数的字符串表示,并返回结果
return Dragon4_Positional_LongDouble_opt(&x, &opt);
}
# 将 obj 转换为 Python 浮点数,并将其值赋给 val
val = PyFloat_AsDouble(obj);
# 如果在转换过程中出现错误,则返回 NULL
if (PyErr_Occurred()) {
return NULL;
}
# 调用 Dragon4 算法计算双精度浮点数的字符串表示,并返回结果
return Dragon4_Positional_Double_opt(&val, &opt);
PyObject *
Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision,
int min_digits, int sign, TrimMode trim, int pad_left,
int exp_digits)
{
npy_double val; // 声明一个双精度浮点数变量 val
Dragon4_Options opt; // 声明 Dragon4_Options 结构体变量 opt
opt.scientific = 1; // 设置 Dragon4_Options 结构体中 scientific 字段为 1,表示科学计数法
opt.digit_mode = digit_mode; // 设置 Dragon4_Options 结构体中 digit_mode 字段为传入的 digit_mode 参数
opt.cutoff_mode = CutoffMode_TotalLength; // 设置 Dragon4_Options 结构体中 cutoff_mode 字段为 CutoffMode_TotalLength
opt.precision = precision; // 设置 Dragon4_Options 结构体中 precision 字段为传入的 precision 参数
opt.min_digits = min_digits; // 设置 Dragon4_Options 结构体中 min_digits 字段为传入的 min_digits 参数
opt.sign = sign; // 设置 Dragon4_Options 结构体中 sign 字段为传入的 sign 参数
opt.trim_mode = trim; // 设置 Dragon4_Options 结构体中 trim_mode 字段为传入的 trim 参数
opt.digits_left = pad_left; // 设置 Dragon4_Options 结构体中 digits_left 字段为传入的 pad_left 参数
opt.digits_right = -1; // 设置 Dragon4_Options 结构体中 digits_right 字段为 -1
opt.exp_digits = exp_digits; // 设置 Dragon4_Options 结构体中 exp_digits 字段为传入的 exp_digits 参数
// 检查 obj 是否为 Half、Float、Double 或 LongDouble 类型的标量,并调用相应的 Dragon4_Scientific_*_opt 函数
if (PyArray_IsScalar(obj, Half)) {
npy_half x = PyArrayScalar_VAL(obj, Half); // 从 PyArray 标量中提取 npy_half 类型的值 x
return Dragon4_Scientific_Half_opt(&x, &opt); // 调用 Dragon4_Scientific_Half_opt 函数处理 x 和 opt
}
else if (PyArray_IsScalar(obj, Float)) {
npy_float x = PyArrayScalar_VAL(obj, Float); // 从 PyArray 标量中提取 npy_float 类型的值 x
return Dragon4_Scientific_Float_opt(&x, &opt); // 调用 Dragon4_Scientific_Float_opt 函数处理 x 和 opt
}
else if (PyArray_IsScalar(obj, Double)) {
npy_double x = PyArrayScalar_VAL(obj, Double); // 从 PyArray 标量中提取 npy_double 类型的值 x
return Dragon4_Scientific_Double_opt(&x, &opt); // 调用 Dragon4_Scientific_Double_opt 函数处理 x 和 opt
}
else if (PyArray_IsScalar(obj, LongDouble)) {
npy_longdouble x = PyArrayScalar_VAL(obj, LongDouble); // 从 PyArray 标量中提取 npy_longdouble 类型的值 x
return Dragon4_Scientific_LongDouble_opt(&x, &opt); // 调用 Dragon4_Scientific_LongDouble_opt 函数处理 x 和 opt
}
val = PyFloat_AsDouble(obj); // 将 Python 对象 obj 转换为双精度浮点数,并赋值给 val
if (PyErr_Occurred()) { // 检查是否发生了 Python 异常
return NULL; // 若有异常发生,返回空指针
}
return Dragon4_Scientific_Double_opt(&val, &opt); // 调用 Dragon4_Scientific_Double_opt 函数处理 val 和 opt,并返回结果
}
#undef DEBUG_ASSERT
.\numpy\numpy\_core\src\multiarray\dragon4.h
/*
* Copyright (c) 2014 Ryan Juckett
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*
* This file contains a modified version of Ryan Juckett's Dragon4
* implementation, obtained from https://www.ryanjuckett.com,
* which has been ported from C++ to C and which has
* modifications specific to printing floats in numpy.
*
* Ryan Juckett's original code was under the Zlib license; he gave numpy
* permission to include it under the MIT license instead.
*/
/* Half binary format */
/* Float binary format */
/* Double binary format */
/* LongDouble binary format */
defined(HAVE_LDOUBLE_IEEE_DOUBLE_BE))
// 定义一个名为 NPY_LONGDOUBLE_BINFMT_NAME 的宏,并设置其值为 Motorola_extended96
defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE))
// 如果定义了具有 IBM 双倍精度浮点的小数类型,设置 NPY_LONGDOUBLE_BINFMT_NAME 为 IBM_double_double
// 如果没有定义任何长双精度表示,抛出错误
typedef enum DigitMode
{
// 将数字四舍五入以打印最短且唯一可识别的数字。
DigitMode_Unique,
// 输出数字的位数,就好像具有无限精度一样。
DigitMode_Exact,
} DigitMode;
typedef enum CutoffMode
{
// 打印最多 cutoffNumber 个有效数字。
CutoffMode_TotalLength,
// 打印小数点后最多 cutoffNumber 个有效数字。
CutoffMode_FractionLength,
} CutoffMode;
typedef enum TrimMode
{
TrimMode_None, // 不修剪零,始终保留小数点。
TrimMode_LeaveOneZero, // 修剪除小数点前的零以外的所有零。
TrimMode_Zeros, // 修剪所有尾随零,保留小数点。
TrimMode_DptZeros, // 修剪尾随零和小数点。
} TrimMode;
PyObject *\
Dragon4_Positional_
CutoffMode cutoff_mode, int precision,\
int min_digits, int sign, TrimMode trim, \
int pad_left, int pad_right);\
PyObject *\
Dragon4_Scientific_
int precision, int min_digits, int sign, \
TrimMode trim, int pad_left, int exp_digits);
// 为 Half、Float、Double、LongDouble 分别声明 Dragon4_Positional 和 Dragon4_Scientific 函数
make_dragon4_typedecl(Half, npy_half)
make_dragon4_typedecl(Float, npy_float)
make_dragon4_typedecl(Double, npy_double)
make_dragon4_typedecl(LongDouble, npy_longdouble)
// 声明 Dragon4_Positional 和 Dragon4_Scientific 函数,参数为通用 PyObject 类型
PyObject *
Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode,
int precision, int min_digits, int sign, TrimMode trim,
int pad_left, int pad_right);
PyObject *
Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision,
int min_digits, int sign, TrimMode trim, int pad_left,
int exp_digits);
.\numpy\numpy\_core\src\multiarray\dtypemeta.c
/* Array Descr Object */
/* 定义一个宏,设置 NumPy 的 API 版本 */
/* 定义一个宏,指示编译器包含多维数组的头文件 */
/* 清除 PY_SSIZE_T 类型的宏定义,确保使用标准的 ssize_t 类型 */
/* 包含 Python 核心头文件 */
/* 包含结构成员定义的头文件 */
/* 包含 NumPy 的数组类型定义 */
/* 包含 NumPy 的数组标量定义 */
/* 包含 NumPy 的数学函数定义 */
/* 包含自定义的头文件 */
/* 包含断言处理的标准头文件 */
/* 定义 dtypemeta_dealloc 函数,用于释放 PyArray_DTypeMeta 对象 */
static void
dtypemeta_dealloc(PyArray_DTypeMeta *self) {
/* 确保不会意外删除静态定义的 DType */
assert(((PyTypeObject *)self)->tp_flags & Py_TPFLAGS_HEAPTYPE);
/* 释放 scalar_type 成员的引用 */
Py_XDECREF(self->scalar_type);
/* 释放 singleton 成员的引用 */
Py_XDECREF(self->singleton);
/* 释放 dt_slots 成员内存 */
Py_XDECREF(NPY_DT_SLOTS(self)->castingimpls);
PyMem_Free(self->dt_slots);
/* 调用 PyType_Type.tp_dealloc 函数释放对象内存 */
PyType_Type.tp_dealloc((PyObject *) self);
}
/* 定义 dtypemeta_alloc 函数,用于分配 PyArray_DTypeMeta 类型对象 */
static PyObject *
dtypemeta_alloc(PyTypeObject *NPY_UNUSED(type), Py_ssize_t NPY_UNUSED(items))
{
/* 抛出类型错误,说明 DTypes 只能通过 NumPy API 创建 */
PyErr_SetString(PyExc_TypeError,
"DTypes can only be created using the NumPy API.");
return NULL;
}
/* 定义 dtypemeta_new 函数,用于创建 PyArray_DTypeMeta 类型对象 */
static PyObject *
dtypemeta_new(PyTypeObject *NPY_UNUSED(type),
PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds))
{
/* 抛出类型错误,说明 Preliminary-API 中不能子类化 DType */
PyErr_SetString(PyExc_TypeError,
"Preliminary-API: Cannot subclass DType.");
return NULL;
}
/* 定义 dtypemeta_init 函数,用于初始化 PyArray_DTypeMeta 类型对象 */
static int
dtypemeta_init(PyTypeObject *NPY_UNUSED(type),
PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds))
{
/* 抛出类型错误,说明 Preliminary-API 中不能使用 __init__ 初始化 DType 类 */
PyErr_SetString(PyExc_TypeError,
"Preliminary-API: Cannot __init__ DType class.");
return -1;
}
/* 定义 dtype_does_not_promote 函数,用于处理 DType 不会提升的情况 */
static PyArray_DTypeMeta *
dtype_does_not_promote(
PyArray_DTypeMeta *NPY_UNUSED(self), PyArray_DTypeMeta *NPY_UNUSED(other))
{
/* 因为 other 肯定不是 self,所以直接返回 Py_NotImplemented */
Py_INCREF(Py_NotImplemented);
return (PyArray_DTypeMeta *)Py_NotImplemented;
}
/* 定义 dtypemeta_discover_as_default 函数,用于发现 PyArray_DTypeMeta 类型的默认描述符 */
NPY_NO_EXPORT PyArray_Descr *
dtypemeta_discover_as_default(
PyArray_DTypeMeta *cls, PyObject *NPY_UNUSED(obj))
{
/* 调用 NPY_DT_CALL_default_descr 函数获取默认描述符 */
return NPY_DT_CALL_default_descr(cls);
}
/* 定义 use_new_as_default 函数,用于使用自身类型创建默认描述符 */
static PyArray_Descr *
use_new_as_default(PyArray_DTypeMeta *self)
{
/* 调用 PyObject_CallObject 创建 self 对象的实例 */
PyObject *res = PyObject_CallObject((PyObject *)self, NULL);
if (res == NULL) {
return NULL;
}
/* 检查返回值是否为有效的 dtype 实例 */
if (!PyArray_DescrCheck(res)) {
/* 如果不是有效的 dtype 实例,抛出运行时错误 */
PyErr_Format(PyExc_RuntimeError,
"Instantiating %S did not return a dtype instance, this is "
"invalid (especially without a custom `default_descr()`).",
self);
Py_DECREF(res);
return NULL;
}
PyArray_Descr *descr = (PyArray_Descr *)res;
"""
* 应该在这里对描述符进行更多的健全性检查,
* 以确保用户没有做出不当行为。但最终,我们对此的控制力有限。
"""
return descr;
/*
* legacy_setitem_using_DType:
* Function for setting an item using a legacy NumPy DType.
* Parameters:
* - obj: Python object representing the data to set.
* - data: Pointer to the data buffer.
* - arr: Pointer to the NumPy array object.
* Returns:
* - Returns -1 on error, 0 otherwise.
* Notes:
* - Checks if arr is NULL and raises an exception if so.
* - Retrieves the setitem function pointer from the NumPy DType descriptor.
*/
static int
legacy_setitem_using_DType(PyObject *obj, void *data, void *arr)
{
if (arr == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"Using legacy SETITEM with NULL array object is only "
"supported for basic NumPy DTypes.");
return -1;
}
PyArrayDTypeMeta_SetItem *setitem;
setitem = NPY_DT_SLOTS(NPY_DTYPE(PyArray_DESCR(arr)))->setitem;
return setitem(PyArray_DESCR(arr), obj, data);
}
/*
* legacy_getitem_using_DType:
* Function for getting an item using a legacy NumPy DType.
* Parameters:
* - data: Pointer to the data buffer.
* - arr: Pointer to the NumPy array object.
* Returns:
* - Returns a Python object on success, NULL on error.
* Notes:
* - Checks if arr is NULL and raises an exception if so.
* - Retrieves the getitem function pointer from the NumPy DType descriptor.
*/
static PyObject *
legacy_getitem_using_DType(void *data, void *arr)
{
if (arr == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"Using legacy SETITEM with NULL array object is only "
"supported for basic NumPy DTypes.");
return NULL;
}
PyArrayDTypeMeta_GetItem *getitem;
getitem = NPY_DT_SLOTS(NPY_DTYPE(PyArray_DESCR(arr)))->getitem;
return getitem(PyArray_DESCR(arr), data);
}
/*
* default_funcs:
* Default array functions structure.
* Notes:
* - Contains pointers to legacy getitem and setitem functions.
*/
PyArray_ArrFuncs default_funcs = {
.getitem = &legacy_getitem_using_DType,
.setitem = &legacy_setitem_using_DType,
};
/*
* dtypemeta_initialize_struct_from_spec:
* Internal function to initialize a DTypeMeta structure from a specification.
* Parameters:
* - DType: Pointer to the DTypeMeta structure to initialize.
* - spec: Pointer to the specification structure.
* - priv: Integer indicating private initialization (non-zero) or public (zero).
* Returns:
* - Returns -1 on error, 0 otherwise.
* Notes:
* - Checks if the DType is already registered and raises an exception if so.
* - Initializes various fields of the DTypeMeta structure based on the spec.
* - Sets default values for DType slots and function pointers.
*/
NPY_NO_EXPORT int
dtypemeta_initialize_struct_from_spec(
PyArray_DTypeMeta *DType, PyArrayDTypeMeta_Spec *spec, int priv)
{
if (DType->dt_slots != NULL) {
PyErr_Format(PyExc_RuntimeError,
"DType %R appears already registered?", DType);
return -1;
}
DType->flags = spec->flags;
DType->dt_slots = PyMem_Calloc(1, sizeof(NPY_DType_Slots));
if (DType->dt_slots == NULL) {
return -1;
}
/* Set default values (where applicable) */
NPY_DT_SLOTS(DType)->discover_descr_from_pyobject =
&dtypemeta_discover_as_default;
NPY_DT_SLOTS(DType)->is_known_scalar_type = (
&python_builtins_are_known_scalar_types);
NPY_DT_SLOTS(DType)->default_descr = use_new_as_default;
NPY_DT_SLOTS(DType)->common_dtype = dtype_does_not_promote;
NPY_DT_SLOTS(DType)->common_instance = NULL;
NPY_DT_SLOTS(DType)->setitem = NULL;
NPY_DT_SLOTS(DType)->getitem = NULL;
NPY_DT_SLOTS(DType)->get_clear_loop = NULL;
NPY_DT_SLOTS(DType)->get_fill_zero_loop = NULL;
NPY_DT_SLOTS(DType)->finalize_descr = NULL;
NPY_DT_SLOTS(DType)->f = default_funcs;
PyType_Slot *spec_slot = spec->slots;
/* invalid type num. Ideally, we get away with it! */
DType->type_num = -1;
/*
* Handle the scalar type mapping.
*/
Py_INCREF(spec->typeobj);
}
DType->scalar_type = spec->typeobj;
if (PyType_GetFlags(spec->typeobj) & Py_TPFLAGS_HEAPTYPE) {
if (PyObject_SetAttrString((PyObject *)DType->scalar_type,
"__associated_array_dtype__", (PyObject *)DType) < 0) {
Py_DECREF(DType);
return -1;
}
}
if (_PyArray_MapPyTypeToDType(DType, DType->scalar_type, 0) < 0) {
Py_DECREF(DType);
return -1;
}
/* 确保 castingimpls 字典已定义(不确定此处是否需要)*/
NPY_DT_SLOTS(DType)->castingimpls = PyDict_New();
if (NPY_DT_SLOTS(DType)->castingimpls == NULL) {
return -1;
}
/*
* 现在,注册当前定义的所有类型转换!
*/
PyArrayMethod_Spec **next_meth_spec = spec->casts;
while (1) {
PyArrayMethod_Spec *meth_spec = *next_meth_spec;
next_meth_spec++;
if (meth_spec == NULL) {
break;
}
/*
* 用户尚不知道 DType 的名称,因此我们必须为他们填写它!
*/
// 将未定义的 dtypes[i] 填充为 DType
for (int i=0; i < meth_spec->nin + meth_spec->nout; i++) {
if (meth_spec->dtypes[i] == NULL) {
meth_spec->dtypes[i] = DType;
}
}
/* 注册转换操作! */
// priv 指示这是否是一个内部调用
int res = PyArray_AddCastingImplementation_FromSpec(meth_spec, priv);
/* 再次进行清理,以防别人有不良想法... */
// 将填充过的 dtypes[i] 重置为空
for (int i=0; i < meth_spec->nin + meth_spec->nout; i++) {
if (meth_spec->dtypes[i] == DType) {
meth_spec->dtypes[i] = NULL;
}
}
if (res < 0) {
return -1;
}
}
return 0;
/**
* tp_is_gc slot of Python types. This is implemented only for documentation
* purposes to indicate and document the subtleties involved.
*
* Python Type objects are either statically created (typical C-Extension type)
* or HeapTypes (typically created in Python).
* HeapTypes have the Py_TPFLAGS_HEAPTYPE flag and are garbage collected.
* Our DTypeMeta instances (`np.dtype` and its subclasses) *may* be HeapTypes
* if the Py_TPFLAGS_HEAPTYPE flag is set (they are created from Python).
* They are not for legacy DTypes or np.dtype itself.
*
* @param self The Python type object (DTypeMeta instance) to check
* @return nonzero if the object is garbage collected
*/
static inline int
dtypemeta_is_gc(PyObject *dtype_class)
{
// Return the result of checking if the type object is garbage collected
return PyType_Type.tp_is_gc(dtype_class);
}
/**
* This function is currently not used, but will probably be necessary
* in the future when we implement HeapTypes (python/dynamically
* defined types). It should be revised at that time.
*
* @param type The DTypeMeta instance to traverse
* @param visit The visit function for garbage collection traversal
* @param arg Custom argument passed to the visit function
* @return Always asserts and does not return a value
*/
static int
dtypemeta_traverse(PyArray_DTypeMeta *type, visitproc visit, void *arg)
{
// Assert conditions related to the legacy status and type object
assert(0);
assert(!NPY_DT_is_legacy(type) && (PyTypeObject *)type != &PyArrayDescr_Type);
// Visit specific attributes of the DTypeMeta instance for garbage collection
Py_VISIT(type->singleton);
Py_VISIT(type->scalar_type);
// Use PyType_Type to traverse the DTypeMeta instance
return PyType_Type.tp_traverse((PyObject *)type, visit, arg);
}
/**
* Create a new instance of a legacy DTypeMeta object with default settings.
*
* @param self The DTypeMeta class instance (Python type object)
* @param args Tuple of arguments passed to the constructor
* @param kwargs Dictionary of keyword arguments passed to the constructor
* @return New instance of the legacy DTypeMeta object or NULL on failure
*/
static PyObject *
legacy_dtype_default_new(PyArray_DTypeMeta *self,
PyObject *args, PyObject *kwargs)
{
/* TODO: This should allow endianness and possibly metadata */
if (NPY_DT_is_parametric(self)) {
// Raise an error if trying to instantiate a parametric legacy DTypeMeta
PyErr_Format(PyExc_TypeError,
"Preliminary-API: Flexible/Parametric legacy DType '%S' can "
"only be instantiated using `np.dtype(...)`", self);
return NULL;
}
if (PyTuple_GET_SIZE(args) != 0 ||
(kwargs != NULL && PyDict_Size(kwargs))) {
// Reject instantiation with arguments other than an empty tuple
PyErr_Format(PyExc_TypeError,
"currently only the no-argument instantiation is supported; "
"use `np.dtype` instead.");
return NULL;
}
// Increment the reference count of the singleton attribute and return it
Py_INCREF(self->singleton);
return (PyObject *)self->singleton;
}
/**
* Create a new instance of a Unicode string DTypeMeta object.
*
* @param self The DTypeMeta class instance (Python type object)
* @param args Tuple of arguments passed to the constructor
* @param kwargs Dictionary of keyword arguments passed to the constructor
* @return New instance of the Unicode string DTypeMeta object or NULL on failure
*/
static PyObject *
string_unicode_new(PyArray_DTypeMeta *self, PyObject *args, PyObject *kwargs)
{
npy_intp size;
static char *kwlist[] = {"", NULL};
// Parse arguments to get the size of the string
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist,
PyArray_IntpFromPyIntConverter, &size)) {
return NULL;
}
// Raise an error if the size of the string is negative
if (size < 0) {
PyErr_Format(PyExc_ValueError,
"Strings cannot have a negative size but a size of "
"%"NPY_INTP_FMT" was given", size);
return NULL;
}
if (self->type_num == NPY_UNICODE) {
if (npy_mul_sizes_with_overflow(&size, size, 4)) {
PyErr_SetString(
PyExc_TypeError,
"Strings too large to store inside array.");
return NULL;
}
}
if (size > NPY_MAX_INT) {
PyErr_SetString(PyExc_TypeError,
"Strings too large to store inside array.");
return NULL;
}
PyArray_Descr *res = PyArray_DescrNewFromType(self->type_num);
if (res == NULL) {
return NULL;
}
res->elsize = (int)size;
return (PyObject *)res;
}
static PyArray_Descr *
nonparametric_discover_descr_from_pyobject(
PyArray_DTypeMeta *cls, PyObject *obj)
{
/* 如果对象是正确的标量类型,则返回单例 */
assert(!NPY_DT_is_parametric(cls));
// 增加引用计数,返回单例描述符
Py_INCREF(cls->singleton);
return cls->singleton;
}
static PyArray_Descr *
string_discover_descr_from_pyobject(
PyArray_DTypeMeta *cls, PyObject *obj)
{
// 初始化项大小为-1
npy_intp itemsize = -1;
// 如果对象是字节对象
if (PyBytes_Check(obj)) {
// 获取字节对象的大小
itemsize = PyBytes_Size(obj);
}
// 如果对象是 Unicode 对象
else if (PyUnicode_Check(obj)) {
// 获取 Unicode 对象的长度
itemsize = PyUnicode_GetLength(obj);
}
// 如果项大小不为-1
if (itemsize != -1) {
// 如果描述符的类型是 NPY_UNICODE
if (cls->type_num == NPY_UNICODE) {
// 将项大小乘以4(Unicode 的字节数)
itemsize *= 4;
}
// 如果项大小超过 NPY_MAX_INT
if (itemsize > NPY_MAX_INT) {
// 设置类型错误异常
PyErr_SetString(PyExc_TypeError,
"string too large to store inside array.");
}
// 从类型号创建新的描述符
PyArray_Descr *res = PyArray_DescrNewFromType(cls->type_num);
// 如果创建失败则返回空
if (res == NULL) {
return NULL;
}
// 设置描述符的元素大小为项大小(强制转换为int类型)
res->elsize = (int)itemsize;
return res;
}
// 否则调用 PyArray_DTypeFromObjectStringDiscovery 函数进行字符串发现
return PyArray_DTypeFromObjectStringDiscovery(obj, NULL, cls->type_num);
}
static PyArray_Descr *
void_discover_descr_from_pyobject(
PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj)
{
// 如果对象是空对象标量
if (PyArray_IsScalar(obj, Void)) {
// 获取空对象的描述符
PyVoidScalarObject *void_obj = (PyVoidScalarObject *)obj;
// 增加空对象描述符的引用计数
Py_INCREF(void_obj->descr);
// 返回空对象的描述符
return (PyArray_Descr *)void_obj->descr;
}
// 如果对象是字节对象
if (PyBytes_Check(obj)) {
// 从类型号创建新的描述符
PyArray_Descr *descr = PyArray_DescrNewFromType(NPY_VOID);
// 如果创建失败则返回空
if (descr == NULL) {
return NULL;
}
// 获取字节对象的大小
Py_ssize_t itemsize = PyBytes_Size(obj);
// 如果字节对象大小超过 NPY_MAX_INT
if (itemsize > NPY_MAX_INT) {
// 设置类型错误异常
PyErr_SetString(PyExc_TypeError,
"byte-like to large to store inside array.");
// 减少描述符的引用计数
Py_DECREF(descr);
return NULL;
}
// 设置描述符的元素大小为字节对象的大小(强制转换为int类型)
descr->elsize = (int)itemsize;
return descr;
}
// 否则格式化设置类型错误异常
PyErr_Format(PyExc_TypeError,
"A bytes-like object is required, not '%s'", Py_TYPE(obj)->tp_name);
return NULL;
}
static PyArray_Descr *
discover_datetime_and_timedelta_from_pyobject(
PyArray_DTypeMeta *cls, PyObject *obj) {
// 如果对象是日期时间或时间增量的标量对象
if (PyArray_IsScalar(obj, Datetime) ||
PyArray_IsScalar(obj, Timedelta)) {
// 声明日期时间元数据指针
PyArray_DatetimeMetaData *meta;
// 从标量对象获取描述符
PyArray_Descr *descr = PyArray_DescrFromScalar(obj);
// 从描述符获取日期时间元数据
meta = get_datetime_metadata_from_dtype(descr);
// 如果日期时间元数据为空则返回空
if (meta == NULL) {
return NULL;
}
// 创建新的日期时间类型描述符
PyArray_Descr *new_descr = create_datetime_dtype(cls->type_num, meta);
// 释放原描述符的引用计数
Py_DECREF(descr);
return new_descr;
}
// 否则查找对象的日期时间类型
else {
return find_object_datetime_type(obj, cls->type_num);
}
}
static PyArray_Descr *
nonparametric_default_descr(PyArray_DTypeMeta *cls)
{
// 增加非参数化描述符的引用计数
Py_INCREF(cls->singleton);
// 返回非参数化描述符
return cls->singleton;
}
/*
* 对于大多数内置(和遗留)的数据类型,canonical 属性意味着确保本机字节顺序。
* (这里我们不关心元数据。)
*/
static PyArray_Descr *
ensure_native_byteorder(PyArray_Descr *descr)
{
// 如果描述符的字节顺序是本机字节顺序,则增加其引用计数并返回该描述符。
if (PyArray_ISNBO(descr->byteorder)) {
Py_INCREF(descr);
return descr;
}
// 否则,返回一个新的具有本机字节顺序的描述符。
else {
return PyArray_DescrNewByteorder(descr, NPY_NATIVE);
}
}
/* 确保单例的副本(以防我们在某处进行了适配) */
static PyArray_Descr *
datetime_and_timedelta_default_descr(PyArray_DTypeMeta *cls)
{
// 返回一个新的描述符,其类型为 cls->singleton。
return PyArray_DescrNew(cls->singleton);
}
static PyArray_Descr *
void_default_descr(PyArray_DTypeMeta *cls)
{
// 创建一个新的描述符,其类型为 cls->singleton。
PyArray_Descr *res = PyArray_DescrNew(cls->singleton);
// 如果创建失败,返回 NULL。
if (res == NULL) {
return NULL;
}
/*
* 对于 `np.array([], dtype="V")` 的遗留行为是使用 "V8"。
* 这是因为 `[]` 使用 `float64` 作为数据类型,然后用它作为空 void 类型的请求大小。
*/
// 设置描述符的元素大小为 8。
res->elsize = 8;
return res;
}
static PyArray_Descr *
string_and_unicode_default_descr(PyArray_DTypeMeta *cls)
{
// 根据 cls->type_num 创建一个新的描述符。
PyArray_Descr *res = PyArray_DescrNewFromType(cls->type_num);
// 如果创建失败,返回 NULL。
if (res == NULL) {
return NULL;
}
// 设置描述符的元素大小为 1。
res->elsize = 1;
// 如果数据类型是 NPY_UNICODE,则将元素大小乘以 4。
if (cls->type_num == NPY_UNICODE) {
res->elsize *= 4;
}
return res;
}
static PyArray_Descr *
string_unicode_common_instance(PyArray_Descr *descr1, PyArray_Descr *descr2)
{
// 如果 descr1 的元素大小大于等于 descr2,则返回确保 canonical 后的 descr1。
if (descr1->elsize >= descr2->elsize) {
return NPY_DT_CALL_ensure_canonical(descr1);
}
// 否则返回确保 canonical 后的 descr2。
else {
return NPY_DT_CALL_ensure_canonical(descr2);
}
}
static PyArray_Descr *
void_ensure_canonical(_PyArray_LegacyDescr *self)
{
// 如果存在子数组,则确保其基类型是 canonical 的。
if (self->subarray != NULL) {
PyArray_Descr *new_base = NPY_DT_CALL_ensure_canonical(
self->subarray->base);
// 如果获取失败,返回 NULL。
if (new_base == NULL) {
return NULL;
}
// 如果新的基类型和旧的基类型相同,直接返回自身。
if (new_base == self->subarray->base) {
/* 只返回自身,无需修改 */
Py_DECREF(new_base);
Py_INCREF(self);
return (PyArray_Descr *)self;
}
// 否则,创建一个新的描述符,并设置新的基类型。
PyArray_Descr *new = PyArray_DescrNew((PyArray_Descr *)self);
if (new == NULL) {
return NULL;
}
Py_SETREF(((_PyArray_LegacyDescr *)new)->subarray->base, new_base);
return new;
}
// 对于非结构化的 void 类型,始终是 canonical 的。
else {
Py_INCREF(self);
return (PyArray_Descr *)self;
}
}
static PyArray_Descr *
void_common_instance(_PyArray_LegacyDescr *descr1, _PyArray_LegacyDescr *descr2)
{
if (descr1->subarray == NULL && descr1->names == NULL &&
descr2->subarray == NULL && descr2->names == NULL) {
if (descr1->elsize != descr2->elsize) {
PyErr_SetString(npy_static_pydata.DTypePromotionError,
"Invalid type promotion with void datatypes of different "
"lengths. Use the `np.bytes_` datatype instead to pad the "
"shorter value with trailing zero bytes.");
return NULL;
}
Py_INCREF(descr1);
return (PyArray_Descr *)descr1;
}
if (descr1->names != NULL && descr2->names != NULL) {
npy_cache_import("numpy._core._internal", "_promote_fields",
&npy_thread_unsafe_state._promote_fields);
if (npy_thread_unsafe_state._promote_fields == NULL) {
return NULL;
}
PyObject *result = PyObject_CallFunctionObjArgs(
npy_thread_unsafe_state._promote_fields,
descr1, descr2, NULL);
if (result == NULL) {
return NULL;
}
if (!PyObject_TypeCheck(result, Py_TYPE(descr1))) {
PyErr_SetString(PyExc_RuntimeError,
"Internal NumPy error: `_promote_fields` did not return "
"a valid descriptor object.");
Py_DECREF(result);
return NULL;
}
return (PyArray_Descr *)result;
}
else if (descr1->subarray != NULL && descr2->subarray != NULL) {
int cmp = PyObject_RichCompareBool(
descr1->subarray->shape, descr2->subarray->shape, Py_EQ);
if (error_converting(cmp)) {
return NULL;
}
if (!cmp) {
PyErr_SetString(npy_static_pydata.DTypePromotionError,
"invalid type promotion with subarray datatypes "
"(shape mismatch).");
return NULL;
}
PyArray_Descr *new_base = PyArray_PromoteTypes(
descr1->subarray->base, descr2->subarray->base);
if (new_base == NULL) {
return NULL;
}
/*
* 如果是相同的数据类型且容器没有变化,可以保留标识和元数据。
* 这可能需要进一步改进。
*/
if (descr1 == descr2 && new_base == descr1->subarray->base) {
Py_DECREF(new_base);
Py_INCREF(descr1);
return (PyArray_Descr *)descr1;
}
PyArray_Descr *new_descr = PyArray_DescrNew((PyArray_Descr *)descr1);
if (new_descr == NULL) {
Py_DECREF(new_base);
return NULL;
}
Py_SETREF(((_PyArray_LegacyDescr *)new_descr)->subarray->base, new_base);
return new_descr;
}
PyErr_SetString(npy_static_pydata.DTypePromotionError,
"invalid type promotion with structured datatype(s).");
return NULL;
/*
* 下面的函数判断Python内置的标量类型是否已知。
* 主要目的是确保不会发生pyfloat->float64->整数的转换。
* 姑且假设子类会被拒绝发现。
* 这个函数仅用于我们发现的作为有效DType的Python标量类。
*/
NPY_NO_EXPORT int
python_builtins_are_known_scalar_types(
PyArray_DTypeMeta *NPY_UNUSED(cls), PyTypeObject *pytype)
{
/*
* 始终接受常见的Python类型,以确保我们不会进行pyfloat->float64->整数的转换。
* 子类希望被拒绝发现。
* 这仅适用于我们发现的作为有效DType的Python标量类。
*/
if (pytype == &PyFloat_Type ||
pytype == &PyLong_Type ||
pytype == &PyBool_Type ||
pytype == &PyComplex_Type ||
pytype == &PyUnicode_Type ||
pytype == &PyBytes_Type)
{
return 1; // 是已知的标量类型
}
return 0; // 不是已知的标量类型
}
/*
* 如果Python内置的标量类型是已知的,返回1;否则返回0。
*/
static int
signed_integers_is_known_scalar_types(
PyArray_DTypeMeta *cls, PyTypeObject *pytype)
{
if (python_builtins_are_known_scalar_types(cls, pytype)) {
return 1; // 是已知的标量类型
}
/* 转换我们的标量(在太大的无符号数和NaN等情况下引发异常)。 */
return PyType_IsSubtype(pytype, &PyGenericArrType_Type);
}
/*
* 如果Python内置的标量类型是已知的,返回1;否则返回0。
*/
static int
datetime_known_scalar_types(
PyArray_DTypeMeta *cls, PyTypeObject *pytype)
{
if (python_builtins_are_known_scalar_types(cls, pytype)) {
return 1; // 是已知的标量类型
}
/*
* 为了能够从任意字符串中识别描述符,datetime 必须负责。
* 否则我们会尝试转换,这种转换并不真正支持这种情况。
* 只有对象数组在这种方式上有特殊情况。
*/
return (PyType_IsSubtype(pytype, &PyBytes_Type) ||
PyType_IsSubtype(pytype, &PyUnicode_Type));
}
/*
* 如果Python内置的标量类型是已知的,返回1;否则返回0。
*/
static int
string_known_scalar_types(
PyArray_DTypeMeta *cls, PyTypeObject *pytype) {
if (python_builtins_are_known_scalar_types(cls, pytype)) {
return 1; // 是已知的标量类型
}
if (PyType_IsSubtype(pytype, &PyDatetimeArrType_Type)) {
/*
* TODO: 这可能应该被弃用或以其他方式解决。
* 不幸的是,必须在 `String->setitem` 中弃用。
*
* 目前datetime不会转换为较短的字符串,但对于任意值的字符串强制使用`str(obj)[:len]`。
* 这意味着`np.array(np.datetime64("2020-01-01"), "U9")`
* 和 `np.array(np.datetime64("2020-01-01")).astype("U9")` 的行为不同。
*/
return 1; // 是已知的标量类型
}
return 0; // 不是已知的标量类型
}
/*
* 下面的一组函数定义了内置类型的常见dtype运算符。
*/
static PyArray_DTypeMeta *
default_builtin_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other)
{
assert(cls->type_num < NPY_NTYPES_LEGACY);
if (NPY_UNLIKELY(!NPY_DT_is_legacy(other))) {
/*
* 处理我们理解的非遗留类型:Python 标量。
* 这些类型可能比具体的不精确类型优先级低,
* 但可以改变结果的类型(复数、浮点数、整数)。
* 如果我们自己的 DType 不是数值类型或者优先级更低(例如整数但抽象类型是浮点数),
* 则信号表示未实现。
*/
if (other == &PyArray_PyComplexDType) {
if (PyTypeNum_ISCOMPLEX(cls->type_num)) {
Py_INCREF(cls);
return cls;
}
else if (cls->type_num == NPY_HALF || cls->type_num == NPY_FLOAT) {
return NPY_DT_NewRef(&PyArray_CFloatDType);
}
else if (cls->type_num == NPY_DOUBLE) {
return NPY_DT_NewRef(&PyArray_CDoubleDType);
}
else if (cls->type_num == NPY_LONGDOUBLE) {
return NPY_DT_NewRef(&PyArray_CLongDoubleDType);
}
}
else if (other == &PyArray_PyFloatDType) {
if (PyTypeNum_ISCOMPLEX(cls->type_num)
|| PyTypeNum_ISFLOAT(cls->type_num)) {
Py_INCREF(cls);
return cls;
}
}
else if (other == &PyArray_PyLongDType) {
if (PyTypeNum_ISCOMPLEX(cls->type_num)
|| PyTypeNum_ISFLOAT(cls->type_num)
|| PyTypeNum_ISINTEGER(cls->type_num)
|| cls->type_num == NPY_TIMEDELTA) {
Py_INCREF(cls);
return cls;
}
}
// 返回未实现的信号
Py_INCREF(Py_NotImplemented);
return (PyArray_DTypeMeta *)Py_NotImplemented;
}
// 如果 other 的类型号大于 cls 的类型号
if (other->type_num > cls->type_num) {
/*
* 让更通用(类型号更大)的 DType 处理此情况
* (注意半精度类型在所有其他类型之后,这里可以正常工作)。
*/
Py_INCREF(Py_NotImplemented);
return (PyArray_DTypeMeta *)Py_NotImplemented;
}
/*
* 注意:可能应该在某个时候重新审视提升表的使用。
* 可能最有用的是完全移除它,然后考虑添加一个快速路径/缓存 `PyArray_CommonDType()` 本身。
*/
// 获取公共类型的类型号
int common_num = _npy_type_promotion_table[cls->type_num][other->type_num];
// 如果没有公共类型,返回未实现的信号
if (common_num < 0) {
Py_INCREF(Py_NotImplemented);
return (PyArray_DTypeMeta *)Py_NotImplemented;
}
// 根据公共类型的类型号返回对应的 DType
return PyArray_DTypeFromTypeNum(common_num);
static PyArray_DTypeMeta *
string_unicode_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other)
{
// 断言确保cls的类型编号小于NPY_NTYPES_LEGACY且不等于other
assert(cls->type_num < NPY_NTYPES_LEGACY && cls != other);
// 如果other不是遗留类型,或者不是数字类型并且(cls是unicode而other是string),则返回NotImplemented
if (!NPY_DT_is_legacy(other) || (!PyTypeNum_ISNUMBER(other->type_num) &&
!(cls->type_num == NPY_UNICODE && other->type_num == NPY_STRING))) {
Py_INCREF(Py_NotImplemented);
return (PyArray_DTypeMeta *)Py_NotImplemented;
}
/*
* 内建类型按复杂性排序(除了object类型)。
* 或许我们不应该把数字和字符串视为“常见”,但目前我们这样做了。
*/
// 增加对cls的引用计数并返回它
Py_INCREF(cls);
return cls;
}
static PyArray_DTypeMeta *
datetime_common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other)
{
/*
* 时间增量和日期时间实际上不应该进行提升。当前这么做意味着我们在比较类型解析器中需要额外的hack。
* 对于比较,我们必须确保优雅地拒绝它,以便返回True/False值的数组。
*/
// 如果cls是日期时间类型且other是时间增量类型,则返回cls
if (cls->type_num == NPY_DATETIME && other->type_num == NPY_TIMEDELTA) {
/*
* TODO: 实际上我们目前允许在这里进行提升。在`np.add(datetime, timedelta)`中当前依赖于此,
* 而对于连接操作,类型转换步骤将失败。
*/
Py_INCREF(cls);
return cls;
}
// 否则调用默认的内建常见类型处理函数
return default_builtin_common_dtype(cls, other);
}
static PyArray_DTypeMeta *
object_common_dtype(
PyArray_DTypeMeta *cls, PyArray_DTypeMeta *NPY_UNUSED(other))
{
/*
* object类型DType是特殊的,它可以表示一切,包括所有潜在的用户定义DType。
* 在此推迟(或出错)的一个原因可能是,其他的DType不支持标量,因此`arr1d[0]`返回一个0-D数组,
* 而`arr.astype(object)`将失败。但对象类型转换是特殊的。
*/
// 增加对cls的引用计数并返回它
Py_INCREF(cls);
return cls;
}
NPY_NO_EXPORT int
dtypemeta_wrap_legacy_descriptor(
_PyArray_LegacyDescr *descr, PyArray_ArrFuncs *arr_funcs,
PyTypeObject *dtype_super_class, const char *name, const char *alias)
{
int has_type_set = Py_TYPE(descr) == &PyArrayDescr_Type;
if (!has_type_set) {
for (int i = 0; i < NPY_NTYPES_LEGACY; i++) {
PyArray_Descr *builtin = PyArray_DescrFromType(i);
has_type_set = Py_TYPE(descr) == Py_TYPE(builtin);
Py_DECREF(builtin);
if (has_type_set) {
break;
}
}
}
if (!has_type_set) {
PyErr_Format(PyExc_RuntimeError,
"在创建/包装旧版本 DType 时,原始类不是 PyArrayDescr_Type (它在这一步被替换)。"
"创建自定义 DType 用于类型 %S 的扩展程序必须修改,以确保 `Py_TYPE(descr) == &PyArrayDescr_Type` 或者一个现有的 dtype (假设它只是被复制过来并且可以被替换)。",
descr->typeobj, Py_TYPE(descr));
return -1;
}
NPY_DType_Slots *dt_slots = PyMem_Malloc(sizeof(NPY_DType_Slots));
// 检查传入的 dt_slots 是否为 NULL,如果是则返回错误码 -1
if (dt_slots == NULL) {
return -1;
}
// 将 dt_slots 的内存清零,大小为 NPY_DType_Slots 结构的大小
memset(dt_slots, '\0', sizeof(NPY_DType_Slots));
// 分配 PyArray_DTypeMeta 类的内存空间
PyArray_DTypeMeta *dtype_class = PyMem_Malloc(sizeof(PyArray_DTypeMeta));
// 如果分配失败,则释放先前分配的 dt_slots 的内存,并返回错误码 -1
if (dtype_class == NULL) {
PyMem_Free(dt_slots);
return -1;
}
/*
* 通过复制原型实例的方式初始化结构字段,除了我们自己的字段之外,
* 在不同的 DType 之间会有变化。
* 特别是任何对象的初始化必须严格从未更改的原型复制以避免复杂性(例如 PyPy)。
* 任何类型的插槽在 PyType_Ready 之前都需要固定,尽管大多数将在那里自动继承。
*/
static PyArray_DTypeMeta prototype = {
{{
PyVarObject_HEAD_INIT(&PyArrayDTypeMeta_Type, 0)
.tp_name = NULL, // 在下面设置
.tp_basicsize = sizeof(_PyArray_LegacyDescr),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_base = NULL, // 在下面设置
.tp_new = (newfunc)legacy_dtype_default_new,
.tp_doc = (
"DType class corresponding to the scalar type and dtype of "
"the same name.\n\n"
"Please see `numpy.dtype` for the typical way to create\n"
"dtype instances and :ref:`arrays.dtypes` for additional\n"
"information."),
},},
.flags = NPY_DT_LEGACY,
/* 进一步的字段在不同的 DType 中不是共同的 */
};
// 将原型实例的内容复制到 dtype_class 中
memcpy(dtype_class, &prototype, sizeof(PyArray_DTypeMeta));
// 设置 Type 的名称和超类
((PyTypeObject *)dtype_class)->tp_name = name;
((PyTypeObject *)dtype_class)->tp_base = dtype_super_class;
dtype_class->dt_slots = dt_slots;
// 让 Python 完成初始化过程
if (PyType_Ready((PyTypeObject *)dtype_class) < 0) {
Py_DECREF(dtype_class);
return -1;
}
// 为 dt_slots 的 castingimpls 字典分配空间
dt_slots->castingimpls = PyDict_New();
// 如果分配失败,则释放 dtype_class 内存,并返回错误码 -1
if (dt_slots->castingimpls == NULL) {
Py_DECREF(dtype_class);
return -1;
}
/*
* 填充在不同 DType 之间变化的 DTypeMeta 信息,
* 任何变量类型信息在 PyType_Ready() 之前需要设置。
*/
dtype_class->singleton = (PyArray_Descr *)descr;
Py_INCREF(descr->typeobj);
dtype_class->scalar_type = descr->typeobj;
dtype_class->type_num = descr->type_num;
dt_slots->f = *arr_funcs;
// 设置默认函数(对大多数 dtype 正确),在下面可能会被覆盖
dt_slots->default_descr = nonparametric_default_descr;
dt_slots->discover_descr_from_pyobject = (
nonparametric_discover_descr_from_pyobject);
dt_slots->is_known_scalar_type = python_builtins_are_known_scalar_types;
dt_slots->common_dtype = default_builtin_common_dtype;
dt_slots->common_instance = NULL;
dt_slots->ensure_canonical = ensure_native_byteorder;
dt_slots->get_fill_zero_loop = NULL;
// 设置 finalize_descr 字段为 NULL
dt_slots->finalize_descr = NULL;
// 如果 dtype_class 的类型编号是有符号整数类型
if (PyTypeNum_ISSIGNED(dtype_class->type_num)) {
// 将 is_known_scalar_type 设置为 signed_integers_is_known_scalar_types 函数
dt_slots->is_known_scalar_type = signed_integers_is_known_scalar_types;
}
// 如果 descr 的类型编号是用户定义的类型
if (PyTypeNum_ISUSERDEF(descr->type_num)) {
// 将 common_dtype 设置为 legacy_userdtype_common_dtype_function 函数
dt_slots->common_dtype = legacy_userdtype_common_dtype_function;
}
// 否则如果 descr 的类型编号是 NPY_OBJECT
else if (descr->type_num == NPY_OBJECT) {
// 将 common_dtype 设置为 object_common_dtype
dt_slots->common_dtype = object_common_dtype;
// 设置 get_fill_zero_loop 为 npy_object_get_fill_zero_loop 函数
dt_slots->get_fill_zero_loop = npy_object_get_fill_zero_loop;
// 设置 get_clear_loop 为 npy_get_clear_object_strided_loop 函数
dt_slots->get_clear_loop = npy_get_clear_object_strided_loop;
}
// 否则如果 descr 的类型编号是日期时间类型
else if (PyTypeNum_ISDATETIME(descr->type_num)) {
// 设置 dtype_class 的 flags 添加 NPY_DT_PARAMETRIC 标志位
dtype_class->flags |= NPY_DT_PARAMETRIC;
// 设置 default_descr 为 datetime_and_timedelta_default_descr 函数
dt_slots->default_descr = datetime_and_timedelta_default_descr;
// 设置 discover_descr_from_pyobject 为 discover_datetime_and_timedelta_from_pyobject 函数
dt_slots->discover_descr_from_pyobject = (
discover_datetime_and_timedelta_from_pyobject);
// 设置 common_dtype 为 datetime_common_dtype
dt_slots->common_dtype = datetime_common_dtype;
// 设置 common_instance 为 datetime_type_promotion
dt_slots->common_instance = datetime_type_promotion;
// 如果 descr 的类型编号是 NPY_DATETIME
if (descr->type_num == NPY_DATETIME) {
// 设置 is_known_scalar_type 为 datetime_known_scalar_types 函数
dt_slots->is_known_scalar_type = datetime_known_scalar_types;
}
}
// 否则如果 descr 的类型编号是灵活类型
else if (PyTypeNum_ISFLEXIBLE(descr->type_num)) {
// 设置 dtype_class 的 flags 添加 NPY_DT_PARAMETRIC 标志位
dtype_class->flags |= NPY_DT_PARAMETRIC;
// 如果 descr 的类型编号是 NPY_VOID
if (descr->type_num == NPY_VOID) {
// 设置 default_descr 为 void_default_descr 函数
dt_slots->default_descr = void_default_descr;
// 设置 discover_descr_from_pyobject 为 void_discover_descr_from_pyobject 函数
dt_slots->discover_descr_from_pyobject = (
void_discover_descr_from_pyobject);
// 设置 common_instance 为 void_common_instance
dt_slots->common_instance = (PyArrayDTypeMeta_CommonInstance *)void_common_instance;
// 设置 ensure_canonical 为 void_ensure_canonical 函数
dt_slots->ensure_canonical = (PyArrayDTypeMeta_EnsureCanonical *)void_ensure_canonical;
// 设置 get_fill_zero_loop 为 npy_get_zerofill_void_and_legacy_user_dtype_loop 函数
dt_slots->get_fill_zero_loop =
(PyArrayMethod_GetTraverseLoop *)npy_get_zerofill_void_and_legacy_user_dtype_loop;
// 设置 get_clear_loop 为 npy_get_clear_void_and_legacy_user_dtype_loop 函数
dt_slots->get_clear_loop =
(PyArrayMethod_GetTraverseLoop *)npy_get_clear_void_and_legacy_user_dtype_loop;
}
// 否则
else {
// 设置 default_descr 为 string_and_unicode_default_descr 函数
dt_slots->default_descr = string_and_unicode_default_descr;
// 设置 is_known_scalar_type 为 string_known_scalar_types 函数
dt_slots->is_known_scalar_type = string_known_scalar_types;
// 设置 discover_descr_from_pyobject 为 string_discover_descr_from_pyobject 函数
dt_slots->discover_descr_from_pyobject = (
string_discover_descr_from_pyobject);
// 设置 common_dtype 为 string_unicode_common_dtype
dt_slots->common_dtype = string_unicode_common_dtype;
// 设置 common_instance 为 string_unicode_common_instance
dt_slots->common_instance = string_unicode_common_instance;
// 将 dtype_class 强制转换为 PyTypeObject 类型,并设置 tp_new 方法为 string_unicode_new 函数
((PyTypeObject*)dtype_class)->tp_new = (newfunc)string_unicode_new;
}
}
// 如果 descr 的类型编号是数字类型
if (PyTypeNum_ISNUMBER(descr->type_num)) {
// 设置 dtype_class 的 flags 添加 NPY_DT_NUMERIC 标志位
dtype_class->flags |= NPY_DT_NUMERIC;
}
// 将 descr 的类型对象映射到 dtype_class,并根据是否是用户定义的类型来决定
if (_PyArray_MapPyTypeToDType(dtype_class, descr->typeobj,
PyTypeNum_ISUSERDEF(dtype_class->type_num)) < 0) {
// 出错时释放 dtype_class,并返回 -1
Py_DECREF(dtype_class);
return -1;
}
// 最后,将 descr 的类设置为 dtype_class
Py_SET_TYPE(descr, (PyTypeObject *)dtype_class);
/* 如果描述符的类型不是用户定义的内置数据类型,则执行以下操作 */
if (!PyTypeNum_ISUSERDEF(descr->type_num)) {
/* 导入 numpy.dtypes 模块中的 _add_dtype_helper 函数,并存储在全局变量中 */
npy_cache_import("numpy.dtypes", "_add_dtype_helper",
&npy_thread_unsafe_state._add_dtype_helper);
/* 如果无法导入 _add_dtype_helper 函数,则返回错误码 */
if (npy_thread_unsafe_state._add_dtype_helper == NULL) {
return -1;
}
/* 调用 _add_dtype_helper 函数,向其传递 dtype_class 对象和 alias 字符串作为参数 */
if (PyObject_CallFunction(
npy_thread_unsafe_state._add_dtype_helper,
"Os", (PyObject *)dtype_class, alias) == NULL) {
return -1;
}
}
/* 操作成功完成,返回成功码 */
return 0;
/*
* 获取抽象类型的属性的函数
*/
static PyObject *
dtypemeta_get_abstract(PyArray_DTypeMeta *self) {
return PyBool_FromLong(NPY_DT_is_abstract(self));
}
/*
* 获取遗留类型的属性的函数
*/
static PyObject *
dtypemeta_get_legacy(PyArray_DTypeMeta *self) {
return PyBool_FromLong(NPY_DT_is_legacy(self));
}
/*
* 获取参数化类型的属性的函数
*/
static PyObject *
dtypemeta_get_parametric(PyArray_DTypeMeta *self) {
return PyBool_FromLong(NPY_DT_is_parametric(self));
}
/*
* 获取是否为数值类型的属性的函数
*/
static PyObject *
dtypemeta_get_is_numeric(PyArray_DTypeMeta *self) {
return PyBool_FromLong(NPY_DT_is_numeric(self));
}
/*
* 定义每个 DType(数据类型)类的公开信息。
* 包含用于访问和设置的属性定义。
*/
static PyGetSetDef dtypemeta_getset[] = {
{"_abstract", (getter)dtypemeta_get_abstract, NULL, NULL, NULL}, // 获取抽象属性的访问器
{"_legacy", (getter)dtypemeta_get_legacy, NULL, NULL, NULL}, // 获取遗留属性的访问器
{"_parametric", (getter)dtypemeta_get_parametric, NULL, NULL, NULL}, // 获取参数化属性的访问器
{"_is_numeric", (getter)dtypemeta_get_is_numeric, NULL, NULL, NULL}, // 获取是否为数值类型属性的访问器
{NULL, NULL, NULL, NULL, NULL} // 结束符号
};
/*
* 定义 DTypeMeta 结构体的成员变量及其描述信息。
*/
static PyMemberDef dtypemeta_members[] = {
{"type",
T_OBJECT, offsetof(PyArray_DTypeMeta, scalar_type), READONLY,
"scalar type corresponding to the DType."}, // DType 对应的标量类型
{NULL, 0, 0, 0, NULL}, // 结束符号
};
/*
* 初始化遗留 DTypeMeta 别名的函数。
* 将 _builtin_descrs 中的每个内置描述符与其对应的 NPY_DTYPE 宏扩展连接。
*/
NPY_NO_EXPORT void
initialize_legacy_dtypemeta_aliases(_PyArray_LegacyDescr **_builtin_descrs) {
_Bool_dtype = NPY_DTYPE(_builtin_descrs[NPY_BOOL]); // 布尔类型的别名
_Byte_dtype = NPY_DTYPE(_builtin_descrs[NPY_BYTE]); // 字节类型的别名
_UByte_dtype = NPY_DTYPE(_builtin_descrs[NPY_UBYTE]); // 无符号字节类型的别名
_Short_dtype = NPY_DTYPE(_builtin_descrs[NPY_SHORT]); // 短整型的别名
_UShort_dtype = NPY_DTYPE(_builtin_descrs[NPY_USHORT]); // 无符号短整型的别名
_Int_dtype = NPY_DTYPE(_builtin_descrs[NPY_INT]); // 整型的别名
_UInt_dtype = NPY_DTYPE(_builtin_descrs[NPY_UINT]); // 无符号整型的别名
_Long_dtype = NPY_DTYPE(_builtin_descrs[NPY_LONG]); // 长整型的别名
_ULong_dtype = NPY_DTYPE(_builtin_descrs[NPY_ULONG]); // 无符号长整型的别名
_LongLong_dtype = NPY_DTYPE(_builtin_descrs[NPY_LONGLONG]); // 长长整型的别名
_ULongLong_dtype = NPY_DTYPE(_builtin_descrs[NPY_ULONGLONG]); // 无符号长长整型的别名
_Int8_dtype = NPY_DTYPE(_builtin_descrs[NPY_INT8]); // 8 位整型的别名
_UInt8_dtype = NPY_DTYPE(_builtin_descrs[NPY_UINT8]); // 无符号 8 位整型的别名
_Int16_dtype = NPY_DTYPE(_builtin_descrs[NPY_INT16]); // 16 位整型的别名
_UInt16_dtype = NPY_DTYPE(_builtin_descrs[NPY_UINT16]); // 无符号 16 位整型的别名
_Int32_dtype = NPY_DTYPE(_builtin_descrs[NPY_INT32]); // 32 位整型的别名
_UInt32_dtype = NPY_DTYPE(_builtin_descrs[NPY_UINT32]); // 无符号 32 位整型的别名
_Int64_dtype = NPY_DTYPE(_builtin_descrs[NPY_INT64]); // 64 位整型的别名
_UInt64_dtype = NPY_DTYPE(_builtin_descrs[NPY_UINT64]); // 无符号 64 位整型的别名
_Intp_dtype = NPY_DTYPE(_builtin_descrs[NPY_INTP]); // 平台相关整型的别名
_UIntp_dtype = NPY_DTYPE(_builtin_descrs[NPY_UINTP]); // 无符号平台相关整型的别名
_DefaultInt_dtype = NPY_DTYPE(_builtin_descrs[NPY_DEFAULT_INT]); // 默认整型的别名
_Half_dtype = NPY_DTYPE(_builtin_descrs[NPY_HALF]); // 半精度浮点数的别名
_Float_dtype = NPY_DTYPE(_builtin_descrs[NPY_FLOAT]); // 单精度浮点数的别名
_Double_dtype = NPY_DTYPE(_builtin_descrs[NPY_DOUBLE]); // 双精度浮点数的别名
_LongDouble_dtype = NPY_DTYPE(_builtin_descrs[NPY_LONGDOUBLE]); // 长双精度浮点数的别名
_CFloat_dtype = NPY_DTYPE(_builtin_descrs[NPY_CFLOAT]); // 复数单精度浮点数的别名
_CDouble_dtype = NPY_DTYPE(_builtin_descrs[NPY_CDOUBLE]); // 复数双精度浮点数的别名
_CLongDouble_dtype = NPY_DTYPE(_builtin_descrs[NPY_CLONGDOUBLE]); // 复数长双精度浮点数的别名
}
// NPY_STRING 是 Python 2 的遗留名称,用于描述字节字符串类型的数据类型
_Bytes_dtype = NPY_DTYPE(_builtin_descrs[NPY_STRING]);
// NPY_UNICODE 是描述 Unicode 字符串类型的数据类型
_Unicode_dtype = NPY_DTYPE(_builtin_descrs[NPY_UNICODE]);
// NPY_DATETIME 是描述日期时间类型的数据类型
_Datetime_dtype = NPY_DTYPE(_builtin_descrs[NPY_DATETIME]);
// NPY_TIMEDELTA 是描述时间间隔类型的数据类型
_Timedelta_dtype = NPY_DTYPE(_builtin_descrs[NPY_TIMEDELTA]);
// NPY_OBJECT 是描述 Python 对象类型的数据类型
_Object_dtype = NPY_DTYPE(_builtin_descrs[NPY_OBJECT]);
// NPY_VOID 是描述 void(空)数据类型的数据类型
_Void_dtype = NPY_DTYPE(_builtin_descrs[NPY_VOID]);
}
NPY_NO_EXPORT PyTypeObject PyArrayDTypeMeta_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "numpy._DTypeMeta",
.tp_basicsize = sizeof(PyArray_DTypeMeta),
.tp_dealloc = (destructor)dtypemeta_dealloc,
/* Types are garbage collected (see dtypemeta_is_gc documentation) */
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_doc = "Preliminary NumPy API: The Type of NumPy DTypes (metaclass)",
.tp_traverse = (traverseproc)dtypemeta_traverse,
.tp_members = dtypemeta_members,
.tp_getset = dtypemeta_getset,
.tp_base = NULL, /* set to PyType_Type at import time */
.tp_init = (initproc)dtypemeta_init,
.tp_alloc = dtypemeta_alloc,
.tp_new = dtypemeta_new,
.tp_is_gc = dtypemeta_is_gc,
};
PyArray_DTypeMeta *_Bool_dtype = NULL;
PyArray_DTypeMeta *_Byte_dtype = NULL;
PyArray_DTypeMeta *_UByte_dtype = NULL;
PyArray_DTypeMeta *_Short_dtype = NULL;
PyArray_DTypeMeta *_UShort_dtype = NULL;
PyArray_DTypeMeta *_Int_dtype = NULL;
PyArray_DTypeMeta *_UInt_dtype = NULL;
PyArray_DTypeMeta *_Long_dtype = NULL;
PyArray_DTypeMeta *_ULong_dtype = NULL;
PyArray_DTypeMeta *_LongLong_dtype = NULL;
PyArray_DTypeMeta *_ULongLong_dtype = NULL;
PyArray_DTypeMeta *_Int8_dtype = NULL;
PyArray_DTypeMeta *_UInt8_dtype = NULL;
PyArray_DTypeMeta *_Int16_dtype = NULL;
PyArray_DTypeMeta *_UInt16_dtype = NULL;
PyArray_DTypeMeta *_Int32_dtype = NULL;
PyArray_DTypeMeta *_UInt32_dtype = NULL;
PyArray_DTypeMeta *_Int64_dtype = NULL;
PyArray_DTypeMeta *_UInt64_dtype = NULL;
PyArray_DTypeMeta *_Intp_dtype = NULL;
PyArray_DTypeMeta *_UIntp_dtype = NULL;
PyArray_DTypeMeta *_DefaultInt_dtype = NULL;
PyArray_DTypeMeta *_Half_dtype = NULL;
PyArray_DTypeMeta *_Float_dtype = NULL;
PyArray_DTypeMeta *_Double_dtype = NULL;
PyArray_DTypeMeta *_LongDouble_dtype = NULL;
PyArray_DTypeMeta *_CFloat_dtype = NULL;
PyArray_DTypeMeta *_CDouble_dtype = NULL;
PyArray_DTypeMeta *_CLongDouble_dtype = NULL;
PyArray_DTypeMeta *_Bytes_dtype = NULL;
PyArray_DTypeMeta *_Unicode_dtype = NULL;
PyArray_DTypeMeta *_Datetime_dtype = NULL;
PyArray_DTypeMeta *_Timedelta_dtype = NULL;
PyArray_DTypeMeta *_Object_dtype = NULL;
PyArray_DTypeMeta *_Void_dtype = NULL;
/*
* NUMPY_API
* 获取 ArrFuncs 结构体,该结构体现在存在于 DType 而非描述符中。
* 应尽量避免使用此结构体,但某些功能仍然需要使用。
*
* 除了检查 NULL 之后的 getitem、setitem、copyswap 和 copyswapn 之外,
* 使用任何插槽都是无效的。通常鼓励检查 NULL。
*
* 由于公共版本是静态内联函数,仅在 2.x 上调用函数,但在 1.x 上直接访问 `descr` 结构,
* 所以此函数使用下划线标记为“私有”版本。一旦 1.x 向后兼容性消失,应直接导出而不带下划线。
* 在内部,我们定义了一个私有内联函数 `PyDataType_GetArrFuncs` 以方便直接访问 `DType` 插槽。
*/
NPY_NO_EXPORT PyArray_ArrFuncs *
_PyDataType_GetArrFuncs(const PyArray_Descr *descr)
{
// 调用公共函数 PyDataType_GetArrFuncs 获取 ArrFuncs 结构体
return PyDataType_GetArrFuncs(descr);
}