在cpython 虛擬機器當中浮點數類型的資料結構定義如下所示:
typedef struct { PyObject_HEAD double ob_fval; } PyFloatObject;
上面的數據結構定義圖示如下:
在上面的資料結構當中最重要的一個欄位就是ob_fval,這個就是真實儲存浮點數的地方。
ob_refcnt 就是物件的參考計數。
ob_type 就是物件的型別。
和我們在前面所討論到的元組和列表物件一樣,在cpython 內部實作float 類型的時候也會為float 物件做一層中間層以加快浮點數的記憶體分配,具體的相關程式碼如下所示:
#define PyFloat_MAXFREELIST 100 static int numfree = 0; static PyFloatObject *free_list = NULL;
在cpython 內部做多會快取100 個float 物件的記憶體空間,如果超過100 就會直接釋放記憶體了,這裡需要注意一點的是只用一個指標就可以將所有的float 物件快取起來,這一點是如何實現的。
這是使用在物件PyFloatObject 當中的struct _typeobject *ob_type; 這個欄位實現的,用這個欄位指向下一個float 物件的記憶體空間,因為在free_list 當中的資料並沒有使用,因此可以利用這個特點節省一些記憶體空間。下面則是建立float 物件的具體過程:
PyObject * PyFloat_FromDouble(double fval) { // 首先查看 free_list 当中是否有空闲的 float 对象 PyFloatObject *op = free_list; if (op != NULL) { // 如果有 那么就将让 free_list 指向 free_list 当中的下一个 float 对象 并且将对应的个数减 1 free_list = (PyFloatObject *) Py_TYPE(op); numfree--; } else { // 否则的话就需要申请内存空间 op = (PyFloatObject*) PyObject_MALLOC(sizeof(PyFloatObject)); if (!op) return PyErr_NoMemory(); } /* Inline PyObject_New */ (void)PyObject_INIT(op, &PyFloat_Type); // PyObject_INIT 这个宏的主要作用是将对象的引用计数设置成 1 op->ob_fval = fval; return (PyObject *) op; }
下面是在cpython 當中浮點數的加法具體實現,整個過程比較簡單就是得到新的值,並且建立一個新的PyFloatObject 對象,並且將這個對象傳回。
static PyObject * float_add(PyObject *v, PyObject *w) { double a,b; CONVERT_TO_DOUBLE(v, a); // CONVERT_TO_DOUBLE 这个宏的主要作用就是将对象的 ob_fval 这个字段的值保存到 a 当中 CONVERT_TO_DOUBLE(w, b); // 这个就是将 w 当中的 ob_fval 字段的值保存到 b 当中 a = a + b; return PyFloat_FromDouble(a); // 创建一个新的 float 对象 并且将这个对象返回 }
同理減法也是一樣的。
static PyObject * float_sub(PyObject *v, PyObject *w) { double a,b; CONVERT_TO_DOUBLE(v, a); CONVERT_TO_DOUBLE(w, b); a = a - b; return PyFloat_FromDouble(a); }
static PyObject * float_mul(PyObject *v, PyObject *w) { double a,b; CONVERT_TO_DOUBLE(v, a); CONVERT_TO_DOUBLE(w, b); PyFPE_START_PROTECT("multiply", return 0) a = a * b; PyFPE_END_PROTECT(a) return PyFloat_FromDouble(a); }
static PyObject * float_div(PyObject *v, PyObject *w) { double a,b; CONVERT_TO_DOUBLE(v, a); CONVERT_TO_DOUBLE(w, b); if (b == 0.0) { PyErr_SetString(PyExc_ZeroDivisionError, "float division by zero"); return NULL; } a = a / b; return PyFloat_FromDouble(a); }
這裡加入了一行輸出語句,這個是為了後面方便我們進行測試的。
static PyObject * float_neg(PyFloatObject *v) { printf("%.2lf 正在进行取反运算\n", v->ob_fval); return PyFloat_FromDouble(-v->ob_fval); }
static PyObject * float_abs(PyFloatObject *v) { printf("%.2lf 正在进行取 abs 运算\n", v->ob_fval); return PyFloat_FromDouble(fabs(v->ob_fval)); }
static int float_bool(PyFloatObject *v) { printf("%.2lf 正在进行取 bool 运算\n", v->ob_fval); return v->ob_fval != 0.0; }
下圖是我們對於 cpython 對程式的修改!
下面是修改之後我們再次對浮點數進行操作的時候的輸出,可以看到的是輸出了我們在上面的程式碼當中加入的語句。
以上是Python虛擬機器中浮點數的實作原理是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!