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 ...@@ -21,7 +21,7 @@ Features added
also when compiled by Cython. also when compiled by Cython.
Patch by Pedro Marques da Luz. (Github issue #2273) 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. * Docstrings of ``cpdef`` enums are now copied to the enum class.
Patch by matham. (Github issue #3805) Patch by matham. (Github issue #3805)
......
...@@ -2635,6 +2635,12 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -2635,6 +2635,12 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
elif func_arg.type is Builtin.bytearray_type: elif func_arg.type is Builtin.bytearray_type:
cfunc_name = "__Pyx_PyByteArray_AsDouble" cfunc_name = "__Pyx_PyByteArray_AsDouble"
utility_code_name = 'pybytes_as_double' 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: else:
cfunc_name = "__Pyx_PyObject_AsDouble" cfunc_name = "__Pyx_PyObject_AsDouble"
utility_code_name = 'pyobject_as_double' utility_code_name = 'pyobject_as_double'
......
...@@ -608,16 +608,12 @@ static double __Pyx__PyObject_AsDouble(PyObject* obj) { ...@@ -608,16 +608,12 @@ static double __Pyx__PyObject_AsDouble(PyObject* obj) {
(void)__Pyx__PyBytes_AsDouble; (void)__Pyx__PyBytes_AsDouble;
#else #else
PyNumberMethods *nb = Py_TYPE(obj)->tp_as_number; 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); return __Pyx_PyBytes_AsDouble(obj);
} else if (PyByteArray_CheckExact(obj)) { } else if (PyByteArray_CheckExact(obj)) {
return __Pyx_PyByteArray_AsDouble(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)) { } else if (likely(nb) && likely(nb->nb_float)) {
float_value = nb->nb_float(obj); float_value = nb->nb_float(obj);
if (likely(float_value) && unlikely(!PyFloat_Check(float_value))) { if (likely(float_value) && unlikely(!PyFloat_Check(float_value))) {
...@@ -643,8 +639,44 @@ bad: ...@@ -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 /////////////// /////////////// 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 double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length);/*proto*/
static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) { static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) {
...@@ -657,8 +689,22 @@ static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) { ...@@ -657,8 +689,22 @@ static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) {
/////////////// pybytes_as_double /////////////// /////////////// 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; 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))) { if (likely(!memchr(start, '_', length))) {
const char *end, *last = start + length; const char *end, *last = start + length;
double value; double value;
...@@ -671,19 +717,7 @@ static double __Pyx__PyBytes_AsDouble(PyObject *bytes, const char* start, Py_ssi ...@@ -671,19 +717,7 @@ static double __Pyx__PyBytes_AsDouble(PyObject *bytes, const char* start, Py_ssi
return value; return value;
} }
} }
// slow fallback return __Pyx_SlowPyString_AsDouble(obj);
#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;
} }
......
...@@ -58,3 +58,61 @@ def from_bytes_literals(): ...@@ -58,3 +58,61 @@ def from_bytes_literals():
(123.0, 123.23, 1e+100) (123.0, 123.23, 1e+100)
""" """
return float(b"123"), float(b"123.23"), float(b"1e100") 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