diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-16-10-00-52.gh-issue-132474.fERfiC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-16-10-00-52.gh-issue-132474.fERfiC.rst new file mode 100644 index 00000000000000..d6d885d89bb0b6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-16-10-00-52.gh-issue-132474.fERfiC.rst @@ -0,0 +1,2 @@ +Optimize rounding speed of :class:`int`'s for large (by magnitude) negative +``ndigits``, when result is zero. Patch by Sergey B Kirpichev. diff --git a/Objects/longobject.c b/Objects/longobject.c index 692312c1ad976c..f276e02de1a456 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -6212,6 +6212,27 @@ int___round___impl(PyObject *self, PyObject *o_ndigits) return long_long(self); } + int64_t n, e; + + if (PyLong_AsInt64(ndigits, &n) < 0) { + Py_DECREF(ndigits); + return NULL; + } + n = -n; + + /* A quick exit, if the result is zero. + * We start from criteria abs(self) < 10**(n - 1). Then, from + * definition of the _PyLong_Frexp() we have + * abs(self) < 10*10**(log10(2)*e) < 10**(e/3 + 1). */ + (void)_PyLong_Frexp((PyLongObject *)self, &e); + assert(e >= 0); + assert(!PyErr_Occurred()); + + if (e/3 + 2 < n) { + Py_DECREF(ndigits); + return _PyLong_GetZero(); + } + /* result = self - divmod_near(self, 10 ** -ndigits)[1] */ PyObject *temp = (PyObject*)long_neg((PyLongObject*)ndigits); Py_SETREF(ndigits, temp);