Skip to content

Commit

Permalink
Bit-twiddling hacks
Browse files Browse the repository at this point in the history
  • Loading branch information
mdboom committed Dec 19, 2023
1 parent 840006e commit 01a12af
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 9 deletions.
7 changes: 6 additions & 1 deletion Include/cpython/longintrepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,14 @@ _PyLong_IsCompact(const PyLongObject* op) {
static inline Py_ssize_t
_PyLong_CompactValue(const PyLongObject *op)
{
// Conditionally negate without a branch trick from bit-twiddling hacks.
// Avraham Plotnitzky, Alfonso De Gregorio, Sean Eron Anderson.
// https://graphics.stanford.edu/~seander/bithacks.html#ConditionalNegate

assert(PyType_HasFeature((op)->ob_base.ob_type, Py_TPFLAGS_LONG_SUBCLASS));
assert(PyUnstable_Long_IsCompact(op));
return ((op->ob_digit[0] & PyLong_IS_NEGATIVE_MASK) ? -1 : 1) * ((Py_ssize_t)op->ob_digit[0] & PyLong_MASK);
Py_ssize_t negate = (op->ob_digit[0] & PyLong_IS_NEGATIVE_MASK) != 0;
return (((Py_ssize_t)op->ob_digit[0] & PyLong_MASK) ^ -negate) + negate;
}

#define PyUnstable_Long_CompactValue _PyLong_CompactValue
Expand Down
12 changes: 8 additions & 4 deletions Include/internal/pycore_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,9 @@ static inline int
_PyLong_NonCompactSign(const PyLongObject *op)
{
assert(PyLong_Check(op));
// assert(!_PyLong_IsCompact(op));
return (op->ob_digit[0] & PyLong_IS_NEGATIVE_MASK) ? -1 : 1;
assert(!_PyLong_IsCompact(op));
Py_ssize_t negate = (op->ob_digit[0] & PyLong_IS_NEGATIVE_MASK) != 0;
return ((Py_ssize_t)1 ^ -negate) + negate;
}

/* Equivalent to _PyLong_DigitCount(op) * _PyLong_NonCompactSign(op) */
Expand All @@ -267,9 +268,11 @@ _PyLong_SignedDigitCount(const PyLongObject *op)
if (op->ob_digit[0] == 0) {
return 0;
} else if (_PyLong_IsCompact(op)) {
return (op->ob_digit[0] & PyLong_IS_NEGATIVE_MASK) ? -1 : 1;
Py_ssize_t negate = (op->ob_digit[0] & PyLong_IS_NEGATIVE_MASK) != 0;
return ((Py_ssize_t)1 ^ -negate) + negate;
} else {
return _PyLong_NonCompactDigitCount(op) * _PyLong_NonCompactSign(op);
Py_ssize_t negate = (op->ob_digit[0] & PyLong_IS_NEGATIVE_MASK) != 0;
return (((Py_ssize_t)op->ob_digit[0] & PyLong_MASK) ^ -negate) + negate;
}
}

Expand All @@ -282,6 +285,7 @@ _PyLong_SetNegative(PyLongObject *op)
static inline void
_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)
{
// MGDTODO: Make versions of this that can only shrink, or shrink and grow
assert(size >= 0);
assert(-1 <= sign && sign <= 1);
assert(sign != 0 || size == 0);
Expand Down
8 changes: 4 additions & 4 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ long_normalize(PyLongObject *v)
static inline void
_PyLong_memset(PyLongObject *ob) {
if (!_PyLong_IsCompact(ob)) {
Py_ssize_t size = _PyLong_DigitCount(ob);
Py_ssize_t size = _PyLong_NonCompactDigitCount(ob);
memset(ob->ob_digit + 1, 0, size * sizeof(digit));
}
}
Expand Down Expand Up @@ -2900,7 +2900,7 @@ PyLong_FromString(const char *str, char **pend, int base)

/* Set sign and normalize */
if (!_PyLong_IsZero(z) && sign < 0) {
_PyLong_FlipSign(z);
_PyLong_SetNegative(z);
}
long_normalize(z);
z = maybe_small_long(z);
Expand Down Expand Up @@ -3628,7 +3628,7 @@ _PyLong_Add(PyLongObject *a, PyLongObject *b)
That also means z is not an element of
small_ints, so negating it in-place is safe. */
assert(Py_REFCNT(z) == 1);
_PyLong_FlipSign(z);
_PyLong_SetNegative(z);
}
}
else
Expand Down Expand Up @@ -3666,7 +3666,7 @@ _PyLong_Subtract(PyLongObject *a, PyLongObject *b)
z = x_add(a, b);
if (z != NULL) {
assert(_PyLong_IsZero(z) || Py_REFCNT(z) == 1);
_PyLong_FlipSign(z);
_PyLong_SetNegative(z);
}
}
}
Expand Down

0 comments on commit 01a12af

Please sign in to comment.