楔子
本篇文章来聊一聊浮点数支持的操作,之前说过实例对象的相关操作都定义在类型对象里面,所以我们需要查看 PyFloat_Type。
// Objects/floatobject.c
PyTypeObject PyFloat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"float",
sizeof(PyFloatObject),
// ......
// 浮点数的 __repr__ 方法
(reprfunc)float_repr, /* tp_repr */
// 浮点数作为数值对象拥有的算术操作
&float_as_number, /* tp_as_number */
// 浮点数的哈希操作
(hashfunc)float_hash, /* tp_hash */
// 浮点数支持的比较操作
float_richcompare, /* tp_richcompare */
// ......
};
还是之前说的,Python 底层的函数命名以及 API 都是很有规律的,举个例子:
tp_repr 字段表示实例对象的字符串格式化,在 PyFloat_Type 里面它被赋值为 float_repr。
tp_hash 字段表示实例对象的哈希操作,在 PyFloat_Type 里面它被赋值为 float_hash。
tp_richcompare 字段表示实例对象的比较操作,所有的比较运算均由该字段负责实现,在 PyFloat_Type 里面它被赋值为 float_richcompare。
下面我们来通过源码看一下底层实现。
浮点数的字符串打印
由于 PyFloat_Type 没有实现 tp_str(字段的值为 0),所以打印一个浮点数会执行 tp_repr,它对应的具体实现为 float_repr 函数。
// Objects/floatobject.c
static PyObject *
float_repr(PyFloatObject *v)
{
PyObject *result; // 返回值
char *buf;
// 将 Python 浮点数转成 C 的浮点数,然后再转成字符串
buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v),
'r', 0,
Py_DTSF_ADD_DOT_0,
NULL);
if (!buf)
return PyErr_NoMemory();
// 基于 C 字符串创建 Python 字符串
result = _PyUnicode_FromASCII(buf, strlen(buf));
// 释放 buf,然后返回
PyMem_Free(buf);
return result;
}
比较简单,当然具体的转换逻辑由 PyOS_double_to_string 函数负责,内部最终会调用 C 的库函数,感兴趣可以看一下。
浮点数的哈希值
获取浮点数的哈希值会执行 tp_hash,它对应的具体实现为 float_hash。
// Objects/floatobject.c
static Py_hash_t
float_hash(PyFloatObject *v)
{
return _Py_HashDouble((PyObject *)v, v->ob_fval);
}
具体的哈希计算逻辑由 _Py_HashDouble 负责,通过 v->ob_fval 拿到 C 浮点数,然后传进去计算哈希值。
感兴趣可以看一下具体的哈希值计算逻辑,该函数位于 Python/pyhash.c 中。
浮点数的比较操作
浮点数之间的比较操作由 tp_richcompare 字段负责实现,该字段的值为 float_richcompare。