python源码阅读-float

前言

前面说过了Number数据共有intfloatcomplex(复数)3种,本章主要介绍下float

类型对象

floatpython中,也是一个对象,也就是类型对象。其定义在Python/bltinmodule.c文件中:

1
2
3
SETBUILTIN("filter",                &PyFilter_Type);
SETBUILTIN("float", &PyFloat_Type);
SETBUILTIN("frozenset", &PyFrozenSet_Type);

也就是对应着PyFloat_Type,其定义在Objects/floatobject.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
PyTypeObject PyFloat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"float",
sizeof(PyFloatObject),
0,
(destructor)float_dealloc, /* tp_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_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 */
};

浮点数对象的创建

对象的创建时通过调用类型对象中的tp_new方法实现的,所以float对象的创建是通过float函数实现的。

再来看一看float_new这个函数,其同样定义在Objects/floatobject.c中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static PyObject *
float_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *x = Py_False; /* Integer zero */
static char *kwlist[] = {"x", 0};

if (type != &PyFloat_Type)
return float_subtype_new(type, args, kwds); /* Wimp out */
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:float", kwlist, &x))
return NULL;
/* If it's a string, but not a string subclass, use
PyFloat_FromString. */
if (PyUnicode_CheckExact(x))
return PyFloat_FromString(x);
return PyNumber_Float(x);
}

最终通过PyFloat_FromStringPyNumber_Float两个函数来完成,一个是通过数字生成浮点型,另一个是通过字符串来生成浮点型。

PyNumber_Float

首先来了解下PyNumber_Float,其定义在Objects/abstract.c文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
PyObject *
PyNumber_Float(PyObject *o)
{
PyNumberMethods *m;

if (o == NULL) {
return null_error();
}

if (PyFloat_CheckExact(o)) {
Py_INCREF(o);
return o;
}
m = o->ob_type->tp_as_number;
if (m && m->nb_float) { /* This should include subclasses of 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)) { /* A float subclass with nb_float == NULL */
return PyFloat_FromDouble(PyFloat_AS_DOUBLE(o));
}
return PyFloat_FromString(o);
}

与其他语言可能有点不同的是,python的默认是以double双精度(64)位来保存浮点数的。

PyFloat_FromString

然后是同样定义在Objects/abstract.c文件中的PyFloat_FromString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
PyObject *
PyFloat_FromString(PyObject *v)
{
const char *s;
PyObject *s_buffer = NULL;
Py_ssize_t len;
Py_buffer view = {NULL, NULL};
PyObject *result = NULL;

if (PyUnicode_Check(v)) {
s_buffer = _PyUnicode_TransformDecimalAndSpaceToASCII(v);
if (s_buffer == NULL)
return NULL;
s = PyUnicode_AsUTF8AndSize(s_buffer, &len);
if (s == NULL) {
Py_DECREF(s_buffer);
return NULL;
}
}
else if (PyBytes_Check(v)) {
s = PyBytes_AS_STRING(v);
len = PyBytes_GET_SIZE(v);
}
else if (PyByteArray_Check(v)) {
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 {
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);
Py_XDECREF(s_buffer);
return result;
}

PyFloat_FromString将数字的字符串表示形式转换为与它等效的有符号浮点数。

float函数

源码注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
.. class:: float([x])

.. index::
single: NaN
single: Infinity

Return a floating point number constructed from a number or string *x*.

If the argument is a string, it should contain a decimal number, optionally
preceded by a sign, and optionally embedded in whitespace. The optional
sign may be ``'+'`` or ``'-'``; a ``'+'`` sign has no effect on the value
produced. The argument may also be a string representing a NaN
(not-a-number), or a positive or negative infinity. More precisely, the
input must conform to the following grammar after leading and trailing
whitespace characters are removed:

.. productionlist::
sign: "+" | "-"
infinity: "Infinity" | "inf"
nan: "nan"
numeric_value: `floatnumber` | `infinity` | `nan`
numeric_string: [`sign`] `numeric_value`

Here ``floatnumber`` is the form of a Python floating-point literal,
described in :ref:`floating`. Case is not significant, so, for example,
"inf", "Inf", "INFINITY" and "iNfINity" are all acceptable spellings for
positive infinity.

Otherwise, if the argument is an integer or a floating point number, a
floating point number with the same value (within Python's floating point
precision) is returned. If the argument is outside the range of a Python
float, an :exc:`OverflowError` will be raised.

For a general Python object ``x``, ``float(x)`` delegates to
``x.__float__()``.

If no argument is given, ``0.0`` is returned.

Examples::

>>> float('+1.23')
1.23
>>> float(' -12345\n')
-12345.0
>>> float('1e-003')
0.001
>>> float('+1E6')
1000000.0
>>> float('-Infinity')
-inf

The float type is described in :ref:`typesnumeric`.

.. versionchanged:: 3.6
Grouping digits with underscores as in code literals is allowed.

中文说明

float([x]) 函数将数字或数字的字符串表示形式转换为与它等效的有符号浮点数。

如果参数x是一个字符串(十进制表示的数字串),数字前面可以添加符号来表示正数,或负数。符号和数字之间不能出现空格,但是符号前面和数字后面允许出现空格。

如果参数x是一个整数或是一个浮点数,则返回与它等效的浮点数;如果x超出了float类型的范围,则引发OverflowError错误。

如果参数x缺省,则返回 0.0

如果参数x是普通的Python对象,float([x]) 返回的是调用 x.__float __() 结果

注意点

  1. 这个函数有一个特别的地方,就是使用infinity或inf来表示无穷大的数。比如+inf是正无穷大,-inf是负无穷大。在这里引入了数学上的无穷大概念,那么无穷大乘以0是等于什么呢?在这里是等于nan,即not a number(不是一个数字)
  2. 参数x可省略

示例

示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> float('+123')
123.0
>>> float('+1.23')
1.23
>>> float(' -12345\n')
-12345.0
>>> float('1e-003')
0.001
>>> float('+1E6')
1000000.0
>>> float('-Infinity')
-inf
>>> float('inf')
inf
>>> float('-inf')
-inf
>>> float('+inf')
inf
>>> float('nan')
nan
>>> float('+nan')
nan
>>> float('-nan')
nan
>>> float()
0.0

示例2:

1
2
3
4
5
6
7
8
class C:
def __init__(self, score):
self.score = score
def __float__(slef):
return 1.0
c = C(100)
f = float(c)
print(f)

运行结果:

1
1.0

参考:python函数每日一讲 - float函数类型转换详解