Commit 795b93a2 authored by Stefan Behnel's avatar Stefan Behnel

Optimise calling float() also on unicode values by returning a C double directly.

(Probably not as optimal since it can lead to de-optimisation when we actually need a PyFloat result.)
parent 99b874cb
......@@ -21,7 +21,7 @@ Features added
also when compiled by Cython.
Patch by Pedro Marques da Luz. (Github issue #2273)
* ``float()`` is optimised for ``bytes`` and ``bytearray`` arguments.
* ``float()`` is optimised for string arguments (str/bytes/bytearray).
* Docstrings of ``cpdef`` enums are now copied to the enum class.
Patch by matham. (Github issue #3805)
......
......@@ -2635,6 +2635,12 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
elif func_arg.type is Builtin.bytearray_type:
cfunc_name = "__Pyx_PyByteArray_AsDouble"
utility_code_name = 'pybytes_as_double'
elif func_arg.type is Builtin.unicode_type:
cfunc_name = "__Pyx_PyUnicode_AsDouble"
utility_code_name = 'pyunicode_as_double'
elif func_arg.type is Builtin.str_type:
cfunc_name = "__Pyx_PyString_AsDouble"
utility_code_name = 'pystring_as_double'
else:
cfunc_name = "__Pyx_PyObject_AsDouble"
utility_code_name = 'pyobject_as_double'
......
......@@ -608,16 +608,12 @@ static double __Pyx__PyObject_AsDouble(PyObject* obj) {
(void)__Pyx__PyBytes_AsDouble;
#else
PyNumberMethods *nb = Py_TYPE(obj)->tp_as_number;
if (PyBytes_CheckExact(obj)) {
if (PyUnicode_CheckExact(obj)) {
return __Pyx_PyUnicode_AsDouble(obj);
} else if (PyBytes_CheckExact(obj)) {
return __Pyx_PyBytes_AsDouble(obj);
} else if (PyByteArray_CheckExact(obj)) {
return __Pyx_PyByteArray_AsDouble(obj);
} else if (PyUnicode_CheckExact(obj)) {
#if PY_MAJOR_VERSION >= 3
float_value = PyFloat_FromString(obj);
#else
float_value = PyFloat_FromString(obj, 0);
#endif
} else if (likely(nb) && likely(nb->nb_float)) {
float_value = nb->nb_float(obj);
if (likely(float_value) && unlikely(!PyFloat_Check(float_value))) {
......@@ -643,8 +639,44 @@ bad:
}
/////////////// pystring_as_double.proto ///////////////
//@requires: pyunicode_as_double
//@requires: pybytes_as_double
static CYTHON_INLINE double __Pyx_PyString_AsDouble(PyObject *obj) {
#if PY_MAJOR_VERSION >= 3
(void)__Pyx_PyBytes_AsDouble;
return __Pyx_PyUnicode_AsDouble(obj);
#else
(void)__Pyx_PyUnicode_AsDouble;
return __Pyx_PyBytes_AsDouble(obj);
#endif
}
/////////////// pyunicode_as_double.proto ///////////////
static CYTHON_INLINE double __Pyx_PyUnicode_AsDouble(PyObject *obj);/*proto*/
/////////////// pyunicode_as_double.proto ///////////////
//@requires: pybytes_as_double
static CYTHON_INLINE double __Pyx_PyUnicode_AsDouble(PyObject *obj) {
#if PY_MAJOR_VERSION >= 3
if (likely(PyUnicode_IS_ASCII(obj))) {
const char *s;
Py_ssize_t length;
s = PyUnicode_AsUTF8AndSize(obj, &length);
return __Pyx__PyBytes_AsDouble(obj, s, length);
}
#endif
return __Pyx_SlowPyString_AsDouble(obj);
}
/////////////// pybytes_as_double.proto ///////////////
static double __Pyx_SlowPyString_AsDouble(PyObject *obj);/*proto*/
static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length);/*proto*/
static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) {
......@@ -657,8 +689,22 @@ static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) {
/////////////// pybytes_as_double ///////////////
static double __Pyx__PyBytes_AsDouble(PyObject *bytes, const char* start, Py_ssize_t length) {
static double __Pyx_SlowPyString_AsDouble(PyObject *obj) {
PyObject *float_value;
#if PY_MAJOR_VERSION >= 3
float_value = PyFloat_FromString(obj);
#else
float_value = PyFloat_FromString(obj, 0);
#endif
if (likely(float_value)) {
double value = PyFloat_AS_DOUBLE(float_value);
Py_DECREF(float_value);
return value;
}
return (double)-1;
}
static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length) {
if (likely(!memchr(start, '_', length))) {
const char *end, *last = start + length;
double value;
......@@ -671,19 +717,7 @@ static double __Pyx__PyBytes_AsDouble(PyObject *bytes, const char* start, Py_ssi
return value;
}
}
// slow fallback
#if PY_MAJOR_VERSION >= 3
float_value = PyFloat_FromString(bytes);
#else
float_value = PyFloat_FromString(bytes, 0);
#endif
if (likely(float_value)) {
double value = PyFloat_AS_DOUBLE(float_value);
Py_DECREF(float_value);
return value;
}
bad:
return (double)-1;
return __Pyx_SlowPyString_AsDouble(obj);
}
......
......@@ -58,3 +58,61 @@ def from_bytes_literals():
(123.0, 123.23, 1e+100)
"""
return float(b"123"), float(b"123.23"), float(b"1e100")
@cython.test_assert_path_exists(
"//CoerceToPyTypeNode",
"//CoerceToPyTypeNode//PythonCapiCallNode",
)
def from_str(s: 'str'):
"""
>>> from_str("123")
123.0
>>> from_str("123.25")
123.25
>>> from_str("123E100")
1.23e+102
"""
return float(s)
@cython.test_assert_path_exists(
"//CoerceToPyTypeNode",
"//CoerceToPyTypeNode//PythonCapiCallNode",
)
def from_str_literals():
"""
>>> from_str_literals()
(123.0, 123.23, 1e+100)
"""
return float("123"), float("123.23"), float("1e100")
@cython.test_assert_path_exists(
"//CoerceToPyTypeNode",
"//CoerceToPyTypeNode//PythonCapiCallNode",
)
def from_unicode(s: 'unicode'):
"""
>>> from_unicode(u"123")
123.0
>>> from_unicode(u"123.25")
123.25
>>> from_unicode(u"123E100")
1.23e+102
>>> from_unicode(u"123.23\\N{PUNCTUATION SPACE}")
123.23
"""
return float(s)
@cython.test_assert_path_exists(
"//CoerceToPyTypeNode",
"//CoerceToPyTypeNode//PythonCapiCallNode",
)
def from_unicode_literals():
"""
>>> from_unicode_literals()
(123.0, 123.23, 1e+100, 123.23)
"""
return float(u"123"), float(u"123.23"), float(u"1e100"), float(u"123.23\N{PUNCTUATION SPACE}")
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment