Skip to content

gh-120446: add PyLong_FlipSign() public function #120489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Doc/c-api/long.rst
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,19 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
.. versionadded:: 3.14


.. c:function:: int PyLong_FlipSign(PyObject **obj)

Flip the sign of the integer object *\*obj*.

On success, revert the sign of the integer (negative will be positive and
vice versa) and return 0.

On failure, return -1 with an exception set. This function always succeeds
if *\*obj* is a :c:type:`PyLongObject` or its subtype.

.. versionadded:: 3.14


.. c:function:: int PyUnstable_Long_IsCompact(const PyLongObject* op)

Return 1 if *op* is compact, 0 otherwise.
Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ New Features
* Add :c:func:`PyLong_GetSign` function to get the sign of :class:`int` objects.
(Contributed by Sergey B Kirpichev in :gh:`116560`.)

* Add :c:func:`PyLong_FlipSign` function to revert the sign of :class:`int`
objects. (Contributed by Sergey B Kirpichev in :gh:`120446`.)

* Add a new :c:type:`PyUnicodeWriter` API to create a Python :class:`str`
object:

Expand Down
14 changes: 13 additions & 1 deletion Include/cpython/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,23 @@ PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op);
0, -1 or +1 for zero, negative or positive integer, respectively.

- On success, set '*sign' to the integer sign, and return 0.
- On failure, set an exception, and return -1. */
- On failure, set an exception, and return -1. This function always
succeeds if v is a PyLongObject or its subtype.
*/
PyAPI_FUNC(int) PyLong_GetSign(PyObject *v, int *sign);

PyAPI_FUNC(int) _PyLong_Sign(PyObject *v);

/* PyLong_FlipSign. Flip the sign of the integer object:
turn negative into positive and vice versa.

On success, revert the sign of the integer and return 0.

On failure, return -1 with an exception set. This function always succeeds
if *v is a PyLongObject or its subtype.
*/
PyAPI_FUNC(int) PyLong_FlipSign(PyObject **v);

/* _PyLong_NumBits. Return the number of bits needed to represent the
absolute value of a long. For example, this returns 1 for 1 and -1, 2
for 2 and -2, and 2 for 3 and -3. It returns 0 for 0.
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_capi/test_long.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,28 @@ def test_long_getsign(self):

# CRASHES getsign(NULL)

def test_long_flipsign(self):
# Test PyLong_FlipSign()
flipsign = _testcapi.pylong_flipsign
getsign = _testcapi.pylong_getsign
a = flipsign(1)
self.assertEqual(getsign(a), -1)
a = flipsign(0)
self.assertEqual(getsign(a), 0)
a = flipsign(-1)
self.assertEqual(getsign(a), 1)
a = flipsign(123456789)
self.assertEqual(getsign(a), -1)
a = flipsign(-123456789)
self.assertEqual(getsign(a), 1)
a = flipsign(IntSubclass(-123456789))
self.assertEqual(getsign(a), 1)

self.assertRaises(TypeError, flipsign, 1.0)
self.assertRaises(TypeError, flipsign, Index(123456789))

# CRASHES flipsign(NULL)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :c:func:`PyLong_FlipSign` function. Patch by Sergey B Kirpichev.
15 changes: 14 additions & 1 deletion Modules/_testcapi/long.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,19 @@ pylong_getsign(PyObject *module, PyObject *arg)
}
return PyLong_FromLong(sign);
}

static PyObject *
pylong_flipsign(PyObject *module, PyObject *arg)
{
NULLABLE(arg);
PyObject *res = arg;
if (PyLong_Check(arg)) {
res = _PyLong_Copy((PyLongObject *)arg);
}
if (PyLong_FlipSign(&res) == -1) {
return NULL;
}
return res;
}

static PyObject *
pylong_aspid(PyObject *module, PyObject *arg)
Expand All @@ -123,6 +135,7 @@ static PyMethodDef test_methods[] = {
{"pylong_asnativebytes", pylong_asnativebytes, METH_VARARGS},
{"pylong_fromnativebytes", pylong_fromnativebytes, METH_VARARGS},
{"pylong_getsign", pylong_getsign, METH_O},
{"pylong_flipsign", pylong_flipsign, METH_O},
{"pylong_aspid", pylong_aspid, METH_O},
{NULL},
};
Expand Down
23 changes: 23 additions & 0 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,29 @@ PyLong_GetSign(PyObject *vv, int *sign)
return 0;
}

int
PyLong_FlipSign(PyObject **vv)
{
if (!PyLong_Check(*vv)) {
PyErr_Format(PyExc_TypeError, "expect int, got %T", *vv);
return -1;
}

PyLongObject *x = (PyLongObject *)*vv;

if (_PyLong_IsCompact(x)) {
stwodigits ival = -medium_value(x);
if (IS_SMALL_INT(ival)) {
Py_DECREF(x);
*vv = get_small_int((sdigit)ival);
return 0;
}
}

_PyLong_FlipSign(x);
return 0;
}

static int
bit_length_digit(digit x)
{
Expand Down
Loading