浮点数的结构体 PyFloatObject
浮点数的结构体PyFloatObject ,定义在头文件 Include/floatobject.h 中,可以看出浮点数底层使用 C 的double类型来存储真正的值。
// Include/floatobject.h
typedef struct {
PyObject_HEAD
double ob_fval; // 底层使用C的double类型,存储真正的浮点数
} PyFloatObject;
// 展开 PyObject_HEAD
typedef struct {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt; // 引用计数 8字节
struct _typeobject *ob_type; // 类型 8字节
double ob_fval; //
}
浮点数内存大小
把PyFloatObject 展开后发现ob_refcnt 是整数型长度 8 字节,ob_type 是指针 长度 8 字节,ob_fval 是 double 型长度 8 字节,所以浮点数的大小是固定的 24 字节,可以在 Python 中验证。
a = 3.14
a.__sizeof__() # 24
import math
math.pi # 3.141592653589793
math.pi.__sizeof__() # 24
浮点数类型对象 PyFloat_Type
浮点数类型的结构体是 PyFloat_Type,定义在头文件 Include/floatobject.c 中。提供了多个函数来检查和操作浮点数对象,包括浮点数的创建float_new、浮点数的销毁float_dealloc、浮点数的函数集float_as_number 包含加减乘除取余等计算、浮点数的哈希计算float_hash、用于生成浮点数字符串表示的函数float_repr 等等。
// Objects/floatobject.c
PyTypeObject PyFloat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"float", // 名称
sizeof(PyFloatObject), /*浮点型占用内存*/
0,
(destructor)float_dealloc, /* 对象的销毁 tp_dealloc 最终调用的是 float_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)float_repr, /* tp_repr */
&float_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)float_hash, /* tp_hash */
0, /* tp_call */
(reprfunc)float_repr, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
float_new__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
float_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
float_methods, /* tp_methods */
0, /* tp_members */
float_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
float_new, /* tp_new */
};
值为 0 的字段表示这些字段在初始化时没有被显式赋值或使用了默认值,即最终调用的时候使用的是PyTypeObject 中对应的值。
- tp_itemsize: 设为 0 表示这个类型对象没有额外的项目空间(通常用于变长对象,如列表)。
- tp_print: 设为 0 表示没有定义打印函数。
- tp_getattr 和 tp_setattr: 设为 0 表示没有定义获取和设置属性的函数。
- tp_compare: 设为 0 表示没有定义比较函数。
- tp_call: 设为 0 表示这个类型对象不能被调用(即 float 对象不是可调用的)。
浮点数创建
创建对象都是调用tp_new 函数,浮点数对应的是float_new,可以看到最终调用了float_new_impl 函数,如果是字符串则调用 PyFloat_FromString 进行创建,否则调用PyNumber_Float,最终还是落在了PyFloat_FromDouble 或者PyFloat_FromString 函数上。
// Objects/clinic/floatobject.c.h
static PyObject *
float_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
PyObject *return_value = NULL; // 定义最终的返回值
PyObject *x = _PyLong_Zero;
if ((type == &PyFloat_Type) &&
!_PyArg_NoKeywords("float", kwargs)) {
goto exit;
}
if (!PyArg_UnpackTuple(args, "float",
0, 1,
&x)) {
goto exit;
}
return_value = float_new_impl(type, x);
exit:
return return_value;
}
// Objects/floatobject.c
static PyObject *
float_new_impl(PyTypeObject *type, PyObject *x)
/*[clinic end generated code: output=ccf1e8dc460ba6ba input=540ee77c204ff87a]*/
{
if (type != &PyFloat_Type)
return float_subtype_new(type, x); /* Wimp out */
/* 如果是字符串类型的浮点数 */
if (PyUnicode_CheckExact(x))
return PyFloat_FromString(x);
return PyNumber_Float(x);
}
// Objects/abstract.c
PyObject *
PyNumber_Float(PyObject *o)
{
PyNumberMethods *m;
if (o == NULL) {
return null_error();
}
if (PyFloat_CheckExact(o)) {
// 如果是hi浮点数类型,引用计数增加后返回
Py_INCREF(o);
return o;
}
m = o->ob_type->tp_as_number;
if (m && m->nb_float) { /* 如果是浮点数的子类型,并且实现了nb_float方法 */
PyObject *res = m->nb_float(o);
double val;
if (!res || PyFloat_CheckExact(res)) {
return res;
}
if (!PyFloat_Check(res)) {
PyErr_Format(PyExc_TypeError,
"%.50s.__float__ returned non-float (type %.50s)",
o->ob_type->tp_name, res->ob_type->tp_name);
Py_DECREF(res);
return NULL;
}
/* Issue #26983: warn if 'res' not of exact type float. */
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"%.50s.__float__ returned non-float (type %.50s). "
"The ability to return an instance of a strict subclass of float "
"is deprecated, and may be removed in a future version of Python.",
o->ob_type->tp_name, res->ob_type->tp_name)) {
Py_DECREF(res);
return NULL;
}
val = PyFloat_AS_DOUBLE(res);
Py_DECREF(res);
return PyFloat_FromDouble(val);
}
if (PyFloat_Check(o)) { /*浮点数的子类型,没实现nb_float方法*,转换成double型再进行初始化 */
return PyFloat_FromDouble(PyFloat_AS_DOUBLE(o));
}
return PyFloat_FromString(o);
}
从创建过程中可以看到,CPython 最终归结到两个创建浮点数的函数,这两个都是 C API 并且是特殊的只针对于创建浮点对象的:
PyFloat_FromDouble
PyAPI_FUNC(PyObject *) PyFloat_FromDouble(double);
通过 C 的 double 来创建浮点对象
// Objects/floatobject.c
PyObject *
PyFloat_FromDouble(double fval)
{
// op指向free_list中的第一个PyFloatObject对象
PyFloatObject *op = free_list;
if (op != NULL) {
//
// 将op对象的ob_type,转为PyFloatObject,当作链表的next指针
// op是PyFloatObject,而它的ob_type本应该是PyFloat_Type
// 将 free_list 指向 op 的 ob_type 所指的对象,并将其转换为PyFloatObject对象
free_list = (PyFloatObject *) Py_TYPE(op);
numfree--;
} else {
// 新建浮点对象,并且动态申请内存,大小为结构体PyFloatObject
op = (PyFloatObject*) PyObject_MALLOC(sizeof(PyFloatObject));
if (!op)
return PyErr_NoMemory();
}
/* Inline PyObject_New */
// 初始化 ob_refcnt为1,ob_type为PyFloat_Type
(void)PyObject_INIT(op, &PyFloat_Type);
// 初始化ob_fval为传进来的参数fval
op->ob_fval = fval;
// 最终返回的泛类型对象
return (PyObject *) op;
}
// Include/objimpl.h
// op 实例对象的指针,typeobj 类型对象的指针
// Py_TYPE(op) = (typeobj) 将op所指对象的类型设置为传参进来的 typeobj
// _Py_NewReference((PyObject *)(op)) 将op所指对象的引用计数器设置为1
#define PyObject_INIT(op, typeobj) \
( Py_TYPE(op) = (typeobj), _Py_NewReference((PyObject *)(op)), (op) )
PyFloat_FromDouble 的创建过程是:
1、创建浮点对象,并分配内存,如果缓存池中有对象的话可以拿过来直接使用,这样为了提高效率,节省内存。
2、使用宏定义 PyObject_INIT 初始化引用计数器和指针类型。
3、给浮点对象的 ob_fval 赋值,最终的值是存在 ob_fval 中的。
4、最终返回泛类型PyObject * 。
PyFloat_FromString
PyAPI_FUNC(PyObject *) PyFloat_FromString(PyObject*);
通过 python 的字符串创建浮点对象
// Objects/floatobject.c
PyObject *
PyFloat_FromString(PyObject *v)
{
const char *s; // 指向输入字符串v的指针
PyObject *s_buffer = NULL; // 临时对象,用于存储转换后的字符串
Py_ssize_t len; // 输入字符串的长度
Py_buffer view = {NULL, NULL}; // 是一个 Py_buffer 结构体,用于处理字节串的情况
PyObject *result = NULL; // 最终返回的 Python 浮点数对象
if (PyUnicode_Check(v)) {
// 将 Unicode 字符串中的十进制数字字符和空格字符转换为其对应的 ASCII 字符
s_buffer = _PyUnicode_TransformDecimalAndSpaceToASCII(v);
if (s_buffer == NULL)
return NULL;
assert(PyUnicode_IS_ASCII(s_buffer));
/* Simply get a pointer to existing ASCII characters. */
// 转换为 UTF-8 编码的 C 字符串
s = PyUnicode_AsUTF8AndSize(s_buffer, &len);
assert(s != NULL);
}
else if (PyBytes_Check(v)) {
// 处理字节的情况,直接获取其内部的 C 字符串和长度
s = PyBytes_AS_STRING(v);
len = PyBytes_GET_SIZE(v);
}
else if (PyByteArray_Check(v)) {
// 处理字节数组的情况,直接获取其内部的 C 字符串和长度
s = PyByteArray_AS_STRING(v);
len = PyByteArray_GET_SIZE(v);
}
else if (PyObject_GetBuffer(v, &view, PyBUF_SIMPLE) == 0) {
// 处理缓冲区字节
s = (const char *)view.buf;
len = view.len;
/* Copy to NUL-terminated buffer. */
s_buffer = PyBytes_FromStringAndSize(s, len);
if (s_buffer == NULL) {
PyBuffer_Release(&view);
return NULL;
}
s = PyBytes_AS_STRING(s_buffer);
}
else {
// 输入对象是其他类型,则抛出 TypeError 异常
PyErr_Format(PyExc_TypeError,
"float() argument must be a string or a number, not '%.200s'",
Py_TYPE(v)->tp_name);
return NULL;
}
// 将字符串转换为浮点数
result = _Py_string_to_number_with_underscores(s, len, "float", v, v,
float_from_string_inner);
// 释放缓冲区并清理临时对象
PyBuffer_Release(&view);
// 缓冲区中对象的引用计数器减1
Py_XDECREF(s_buffer);
return result;
}
// 将字符串转换为浮点对象
static PyObject *
float_from_string_inner(const char *s, Py_ssize_t len, void *obj)
{
double x;
const char *end;
const char *last = s + len;
/* strip space 在字符串末尾跳过空白字符,确保整个字符串都被正确解析。 */
while (s < last && Py_ISSPACE(*s)) {
s++;
}
while (s < last - 1 && Py_ISSPACE(last[-1])) {
last--;
}
/* We don't care about overflow or underflow. If the platform
* supports them, infinities and signed zeroes (on underflow) are
* fine.
将字符串转换为 double 类型。
*/
x = PyOS_string_to_double(s, (char **)&end, NULL);
if (end != last) {
PyErr_Format(PyExc_ValueError,
"could not convert string to float: "
"%R", obj);
return NULL;
}
else if (x == -1.0 && PyErr_Occurred()) {
return NULL;
}
else {
return PyFloat_FromDouble(x);
}
}
PyFloat_FromDouble 的创建过程是:
- 字符串检查和转换:
- 检查输入对象是否为 Unicode 字符串或字节串。
- 如果是 Unicode 字符串,则将其转换为 UTF-8 编码的 C 字符串。
- 如果是字节串,则直接获取其内部的 C 字符串。
- 如果是其他类型抛出异常。
- 字符串转换为浮点数:
- 调用_Py_string_to_number_with_underscores 和 float_from_string_inner 函数,将字符串转换为 double 类型。
- 可以看到 float_from_string_inner 中调用 PyOS_string_to_double,将字符串转换为 double 型,函数最终落在了 PyFloat_FromDouble 上。
- 清理缓冲区:
- 浮点对象创建完成后,释放缓冲区,清理缓冲区的引用计数。
浮点数销毁
销毁对象是调用指针 tp_dealloc 所指的函数,浮点数对象对应的就是 float_dealloc
// Objects/floatobject.c
static void
float_dealloc(PyFloatObject *op)
{
if (PyFloat_CheckExact(op)) {
// 缓存池满了,不能放了,就直接销毁对象
if (numfree >= PyFloat_MAXFREELIST) {
PyObject_FREE(op);
return;
}
// 缓存池没满,就放到缓存池中
numfree++;
// free_list 是 PyFloatObject 类型的 将 free_list 转换为_typeobject
// 然后让 op的ob_type指向free_list
Py_TYPE(op) = (struct _typeobject *)free_list;
// 链表操作,让free_list指向链表的头部 op是 PyFloatObject类型的
free_list = op;
}
else
// 不是PyFloat_Type类型,通过该对象的tp_free直接释放
Py_TYPE(op)->tp_free((PyObject *)op);
}
浮点数的缓存机制
浮点数的缓存机制是以链表形式存在的,free_list 是PyFloatObject 类型的,所有回收的浮点对象都链在了free_list 上,numfree 则记录了链表中可用的对象个数,PyFloat_MAXFREELIST 表示缓存的容量。
// Objects/floatobject.c
#ifndef PyFloat_MAXFREELIST
#define PyFloat_MAXFREELIST 100 // 限制 free_list 最大数量
#endif
static int numfree = 0; // 记录free_list中可用对象的个数
// 定义链表,类型为PyFloatObject,ob_type指向的应该是PyFloat_Type,
// 在链表中对象的ob_type指向的是下一个PyFloatObject
static PyFloatObject *free_list = NULL;
// PyFloat_FromDouble 函数中 获取缓存
// op指向free_list中的第一个PyFloatObject对象
PyFloatObject *op = free_list;
if (op != NULL) {
//
// 将op对象的ob_type,转为PyFloatObject,当作链表的next指针
// op是PyFloatObject,而它的ob_type本应该是PyFloat_Type
// 将 free_list 指向 op 的 ob_type 所指的对象,并将其转换为PyFloatObject对象
free_list = (PyFloatObject *) Py_TYPE(op);
numfree--;
}
// float_dealloc 函数中,放入缓存
// 缓存池满了,不能放了,就直接销毁对象
if (numfree >= PyFloat_MAXFREELIST) {
PyObject_FREE(op);
return;
}
// 缓存池没满,就放到缓存池中
numfree++;
// free_list 是 PyFloatObject 类型的 将 free_list 转换为_typeobject
// 然后让 op的ob_type指向free_list
Py_TYPE(op) = (struct _typeobject *)free_list;
// 链表操作,让free_list指向链表的头部 op是 PyFloatObject类型的
free_list = op;
缓存的工作方式
1、 当需要创建一个新的浮点数对象时,解释器会首先检查free_list 链表中是否有可用的对象。
2、 如果有,则直接返回缓存链表中的第一个,如果没有,则重新创建一个新的浮点数对象。
3、当一个浮点数对象不再被引用时(例如引用计数归零),它可能会被销毁并释放内存。
4、释放时,如果缓存池的长度超过了PyFloat_MAXFREELIST 则直接销毁,否则将对象链到free_list 上。
备注:
1、将对象放入缓存池中时,会将要销毁的 op 对象放到 free_list 头部,使用 op 的 ob_type 当作 next 指针指向 free_list,ob_type 是 _typeobject(PyFloat_Type)类型,但是 free_list 是PyFloatObject 类型,所以为了节省性能会将free_list 转换成_typeobject。
2、从缓存池中获取对象时,也是获取free_list 头部对象,先将 op 指向free_list,再将free_list 指向 op 的ob_type 所指的对象,并且将其转换为PyFloatObject 类型。
浮点数的属性和函数
PyFloat_Type 中还定义了一些浮点数相关的属性和函数等
float_hash 浮点数的哈希值
/*
a = 3.14
hash(a) # 322818021289917443
*/
// Objects/floatobject.c
static Py_hash_t
float_hash(PyFloatObject *v)
{
// 计算ob_fval的哈希值
return _Py_HashDouble(v->ob_fval);
}
float_richcompare 浮点数的比较
// Objects/floatobject.c
static PyObject*
float_richcompare(PyObject *v, PyObject *w, int op)
{
double i, j;
int r = 0;
assert(PyFloat_Check(v));
i = PyFloat_AS_DOUBLE(v);
/* Switch on the type of w. Set i and j to doubles to be compared,
* and op to the richcomp to use.
*/
if (PyFloat_Check(w))
j = PyFloat_AS_DOUBLE(w);
else if (!Py_IS_FINITE(i)) {
if (PyLong_Check(w))
/* If i is an infinity, its magnitude exceeds any
* finite integer, so it doesn't matter which int we
* compare i with. If i is a NaN, similarly.
*/
j = 0.0;
else
goto Unimplemented;
}
else if (PyLong_Check(w)) {
int vsign = i == 0.0 ? 0 : i < 0.0 ? -1 : 1;
int wsign = _PyLong_Sign(w);
size_t nbits;
int exponent;
if (vsign != wsign) {
/* Magnitudes are irrelevant -- the signs alone
* determine the outcome.
*/
i = (double)vsign;
j = (double)wsign;
goto Compare;
}
/* The signs are the same. */
/* Convert w to a double if it fits. In particular, 0 fits. */
nbits = _PyLong_NumBits(w);
if (nbits == (size_t)-1 && PyErr_Occurred()) {
/* This long is so large that size_t isn't big enough
* to hold the # of bits. Replace with little doubles
* that give the same outcome -- w is so large that
* its magnitude must exceed the magnitude of any
* finite float.
*/
PyErr_Clear();
i = (double)vsign;
assert(wsign != 0);
j = wsign * 2.0;
goto Compare;
}
if (nbits <= 48) {
j = PyLong_AsDouble(w);
/* It's impossible that <= 48 bits overflowed. */
assert(j != -1.0 || ! PyErr_Occurred());
goto Compare;
}
assert(wsign != 0); /* else nbits was 0 */
assert(vsign != 0); /* if vsign were 0, then since wsign is
* not 0, we would have taken the
* vsign != wsign branch at the start */
/* We want to work with non-negative numbers. */
if (vsign < 0) {
/* "Multiply both sides" by -1; this also swaps the
* comparator.
*/
i = -i;
op = _Py_SwappedOp[op];
}
assert(i > 0.0);
(void) frexp(i, &exponent);
/* exponent is the # of bits in v before the radix point;
* we know that nbits (the # of bits in w) > 48 at this point
*/
if (exponent < 0 || (size_t)exponent < nbits) {
i = 1.0;
j = 2.0;
goto Compare;
}
if ((size_t)exponent > nbits) {
i = 2.0;
j = 1.0;
goto Compare;
}
/* v and w have the same number of bits before the radix
* point. Construct two ints that have the same comparison
* outcome.
*/
{
double fracpart;
double intpart;
PyObject *result = NULL;
PyObject *vv = NULL;
PyObject *ww = w;
if (wsign < 0) {
ww = PyNumber_Negative(w);
if (ww == NULL)
goto Error;
}
else
Py_INCREF(ww);
fracpart = modf(i, &intpart);
vv = PyLong_FromDouble(intpart);
if (vv == NULL)
goto Error;
if (fracpart != 0.0) {
/* Shift left, and or a 1 bit into vv
* to represent the lost fraction.
*/
PyObject *temp;
temp = PyNumber_Lshift(ww, _PyLong_One);
if (temp == NULL)
goto Error;
Py_DECREF(ww);
ww = temp;
temp = PyNumber_Lshift(vv, _PyLong_One);
if (temp == NULL)
goto Error;
Py_DECREF(vv);
vv = temp;
temp = PyNumber_Or(vv, _PyLong_One);
if (temp == NULL)
goto Error;
Py_DECREF(vv);
vv = temp;
}
r = PyObject_RichCompareBool(vv, ww, op);
if (r < 0)
goto Error;
result = PyBool_FromLong(r);
Error:
Py_XDECREF(vv);
Py_XDECREF(ww);
return result;
}
} /* else if (PyLong_Check(w)) */
else /* w isn't float or int */
goto Unimplemented;
Compare:
PyFPE_START_PROTECT("richcompare", return NULL)
switch (op) {
case Py_EQ:
r = i == j;
break;
case Py_NE:
r = i != j;
break;
case Py_LE:
r = i <= j;
break;
case Py_GE:
r = i >= j;
break;
case Py_LT:
r = i < j;
break;
case Py_GT:
r = i > j;
break;
}
PyFPE_END_PROTECT(r)
return PyBool_FromLong(r);
Unimplemented:
Py_RETURN_NOTIMPLEMENTED;
}
float_as_number 结构体是所有浮点数函数集,包含加、减、乘、除、取余、幂等
// Objects/floatobject.c
static PyNumberMethods float_as_number = {
float_add, /* nb_add */
float_sub, /* nb_subtract */
float_mul, /* nb_multiply */
float_rem, /* nb_remainder */
float_divmod, /* nb_divmod */
float_pow, /* nb_power */
(unaryfunc)float_neg, /* nb_negative */
float_float, /* nb_positive */
(unaryfunc)float_abs, /* nb_absolute */
(inquiry)float_bool, /* nb_bool */
0, /* nb_invert */
0, /* nb_lshift */
0, /* nb_rshift */
0, /* nb_and */
0, /* nb_xor */
0, /* nb_or */
float___trunc___impl, /* nb_int */
0, /* nb_reserved */
float_float, /* nb_float */
0, /* nb_inplace_add */
0, /* nb_inplace_subtract */
0, /* nb_inplace_multiply */
0, /* nb_inplace_remainder */
0, /* nb_inplace_power */
0, /* nb_inplace_lshift */
0, /* nb_inplace_rshift */
0, /* nb_inplace_and */
0, /* nb_inplace_xor */
0, /* nb_inplace_or */
float_floor_div, /* nb_floor_divide */
float_div, /* nb_true_divide */
0, /* nb_inplace_floor_divide */
0, /* nb_inplace_true_divide */
};
介绍下加法运算 float_add,其他函数都是类似的可以在Objects/floatobject.c 中查看。
// Objects/floatobject.c
static PyObject *
float_add(PyObject *v, PyObject *w)
{
// 先将python对象转换成C的double
double a,b;
// 结构体无法运算,所以通过PyFloat_AS_DOUBLE 将PyFloatObject中的ob_fval值抽取出来
// v的值赋给a,w的值赋给b
CONVERT_TO_DOUBLE(v, a);
CONVERT_TO_DOUBLE(w, b);
// 保护浮点数计算,以确保即使发生异常,程序也不会崩溃
PyFPE_START_PROTECT("add", return 0)
a = a + b;
PyFPE_END_PROTECT(a)
// 计算完后转换成PyFloatObject对象
return PyFloat_FromDouble(a);
}
浮点数相关的宏定义和函数
// 判断是否是PyFloatObject 或者 PyFloatObject 的子类型
#define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type)
// 判断是否是 PyFloatObject 类型
#define PyFloat_CheckExact(op) (Py_TYPE(op) == &PyFloat_Type)
// 返回一个 op 内容的 C double 即ob_fval的值,但没有错误检查
#define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval)
/*
返回一个 C double 代表 op 的内容。 如果 op 不是一个 Python 浮点数对象但是具有 __float__() 方法,
此方法将首先被调用,将 op 转换成一个数点数。 如果 __float__() 未定义则将回退至 __index__()。
如果失败,此方法将返回 -1.0,因此开发者应当调用 PyErr_Occurred() 来检查错误
*/
double
PyFloat_AsDouble(PyObject *op)
{
PyNumberMethods *nb;
PyObject *res;
double val;
if (op == NULL) {
PyErr_BadArgument();
return -1;
}
if (PyFloat_Check(op)) {
return PyFloat_AS_DOUBLE(op);
}
nb = Py_TYPE(op)->tp_as_number;
if (nb == NULL || nb->nb_float == NULL) {
PyErr_Format(PyExc_TypeError, "must be real number, not %.50s",
op->ob_type->tp_name);
return -1;
}
res = (*nb->nb_float) (op);
if (res == NULL) {
return -1;
}
if (!PyFloat_CheckExact(res)) {
if (!PyFloat_Check(res)) {
PyErr_Format(PyExc_TypeError,
"%.50s.__float__ returned non-float (type %.50s)",
op->ob_type->tp_name, res->ob_type->tp_name);
Py_DECREF(res);
return -1;
}
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"%.50s.__float__ returned non-float (type %.50s). "
"The ability to return an instance of a strict subclass of float "
"is deprecated, and may be removed in a future version of Python.",
op->ob_type->tp_name, res->ob_type->tp_name)) {
Py_DECREF(res);
return -1;
}
}
val = PyFloat_AS_DOUBLE(res);
Py_DECREF(res);
return val;
}