CPython源码学习3:浮点数对象

浮点数的结构体 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_getattrtp_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 的创建过程是:

  1. 字符串检查和转换
  • 检查输入对象是否为 Unicode 字符串或字节串。
  • 如果是 Unicode 字符串,则将其转换为 UTF-8 编码的 C 字符串。
  • 如果是字节串,则直接获取其内部的 C 字符串。
  • 如果是其他类型抛出异常。
  1. 字符串转换为浮点数
  • 调用_Py_string_to_number_with_underscores 和 float_from_string_inner 函数,将字符串转换为 double 类型。
  • 可以看到 float_from_string_inner 中调用 PyOS_string_to_double,将字符串转换为 double 型,函数最终落在了 PyFloat_FromDouble 上。
  1. 清理缓冲区
  • 浮点对象创建完成后,释放缓冲区,清理缓冲区的引用计数。

浮点数销毁

销毁对象是调用指针 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;
}

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骇客567

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值