[3.13] gh-102431: Clarify constraints on operands of Decimal logical operations (GH-102836) (#140106)

* [3.13] gh-102431: Clarify constraints on operands of Decimal logical operations (GH-102836)

Sync C/Python implementation of the decimal: logical_ops for contexts.
(cherry picked from commit 6ecf77dbde)

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
This commit is contained in:
Sergey B Kirpichev 2025-10-14 16:45:02 +03:00 committed by GitHub
parent 7992a29512
commit bfb9639352
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 98 additions and 12 deletions

View file

@ -3302,7 +3302,10 @@ def _fill_logical(self, context, opa, opb):
return opa, opb
def logical_and(self, other, context=None):
"""Applies an 'and' operation between self and other's digits."""
"""Applies an 'and' operation between self and other's digits.
Both self and other must be logical numbers.
"""
if context is None:
context = getcontext()
@ -3319,14 +3322,20 @@ def logical_and(self, other, context=None):
return _dec_from_triple(0, result.lstrip('0') or '0', 0)
def logical_invert(self, context=None):
"""Invert all its digits."""
"""Invert all its digits.
The self must be logical number.
"""
if context is None:
context = getcontext()
return self.logical_xor(_dec_from_triple(0,'1'*context.prec,0),
context)
def logical_or(self, other, context=None):
"""Applies an 'or' operation between self and other's digits."""
"""Applies an 'or' operation between self and other's digits.
Both self and other must be logical numbers.
"""
if context is None:
context = getcontext()
@ -3343,7 +3352,10 @@ def logical_or(self, other, context=None):
return _dec_from_triple(0, result.lstrip('0') or '0', 0)
def logical_xor(self, other, context=None):
"""Applies an 'xor' operation between self and other's digits."""
"""Applies an 'xor' operation between self and other's digits.
Both self and other must be logical numbers.
"""
if context is None:
context = getcontext()

View file

@ -0,0 +1,2 @@
Clarify constraints for "logical" arguments in methods of
:class:`decimal.Context`.

View file

@ -282,22 +282,26 @@ an infinity then Decimal('Infinity') is returned.\n\
PyDoc_STRVAR(doc_logical_and,
"logical_and($self, /, other, context=None)\n--\n\n\
Return the digit-wise 'and' of the two (logical) operands.\n\
Applies an 'and' operation between self and other's digits.\n\n\
Both self and other must be logical numbers.\n\
\n");
PyDoc_STRVAR(doc_logical_invert,
"logical_invert($self, /, context=None)\n--\n\n\
Return the digit-wise inversion of the (logical) operand.\n\
Invert all its digits.\n\n\
The self must be logical number.\n\
\n");
PyDoc_STRVAR(doc_logical_or,
"logical_or($self, /, other, context=None)\n--\n\n\
Return the digit-wise 'or' of the two (logical) operands.\n\
Applies an 'or' operation between self and other's digits.\n\n\
Both self and other must be logical numbers. \n\
\n");
PyDoc_STRVAR(doc_logical_xor,
"logical_xor($self, /, other, context=None)\n--\n\n\
Return the digit-wise 'exclusive or' of the two (logical) operands.\n\
Applies an 'xor' operation between self and other's digits.\n\n\
Both self and other must be logical numbers.\n\
\n");
PyDoc_STRVAR(doc_max,
@ -702,22 +706,90 @@ Return the exponent of the magnitude of the operand's MSD.\n\
PyDoc_STRVAR(doc_ctx_logical_and,
"logical_and($self, x, y, /)\n--\n\n\
Digit-wise and of x and y.\n\
Applies the logical operation 'and' between each operand's digits.\n\n\
The operands must be both logical numbers.\n\n\
>>> ExtendedContext.logical_and(Decimal('0'), Decimal('0'))\n\
Decimal('0')\n\
>>> ExtendedContext.logical_and(Decimal('0'), Decimal('1'))\n\
Decimal('0')\n\
>>> ExtendedContext.logical_and(Decimal('1'), Decimal('0'))\n\
Decimal('0')\n\
>>> ExtendedContext.logical_and(Decimal('1'), Decimal('1'))\n\
Decimal('1')\n\
>>> ExtendedContext.logical_and(Decimal('1100'), Decimal('1010'))\n\
Decimal('1000')\n\
>>> ExtendedContext.logical_and(Decimal('1111'), Decimal('10'))\n\
Decimal('10')\n\
>>> ExtendedContext.logical_and(110, 1101)\n\
Decimal('100')\n\
>>> ExtendedContext.logical_and(Decimal(110), 1101)\n\
Decimal('100')\n\
>>> ExtendedContext.logical_and(110, Decimal(1101))\n\
Decimal('100')\n\
\n");
PyDoc_STRVAR(doc_ctx_logical_invert,
"logical_invert($self, x, /)\n--\n\n\
Invert all digits of x.\n\
Invert all the digits in the operand.\n\n\
The operand must be a logical number.\n\n\
>>> ExtendedContext.logical_invert(Decimal('0'))\n\
Decimal('111111111')\n\
>>> ExtendedContext.logical_invert(Decimal('1'))\n\
Decimal('111111110')\n\
>>> ExtendedContext.logical_invert(Decimal('111111111'))\n\
Decimal('0')\n\
>>> ExtendedContext.logical_invert(Decimal('101010101'))\n\
Decimal('10101010')\n\
>>> ExtendedContext.logical_invert(1101)\n\
Decimal('111110010')\n\
\n");
PyDoc_STRVAR(doc_ctx_logical_or,
"logical_or($self, x, y, /)\n--\n\n\
Digit-wise or of x and y.\n\
Applies the logical operation 'or' between each operand's digits.\n\n\
The operands must be both logical numbers.\n\n\
>>> ExtendedContext.logical_or(Decimal('0'), Decimal('0'))\n\
Decimal('0')\n\
>>> ExtendedContext.logical_or(Decimal('0'), Decimal('1'))\n\
Decimal('1')\n\
>>> ExtendedContext.logical_or(Decimal('1'), Decimal('0'))\n\
Decimal('1')\n\
>>> ExtendedContext.logical_or(Decimal('1'), Decimal('1'))\n\
Decimal('1')\n\
>>> ExtendedContext.logical_or(Decimal('1100'), Decimal('1010'))\n\
Decimal('1110')\n\
>>> ExtendedContext.logical_or(Decimal('1110'), Decimal('10'))\n\
Decimal('1110')\n\
>>> ExtendedContext.logical_or(110, 1101)\n\
Decimal('1111')\n\
>>> ExtendedContext.logical_or(Decimal(110), 1101)\n\
Decimal('1111')\n\
>>> ExtendedContext.logical_or(110, Decimal(1101))\n\
Decimal('1111')\n\
\n");
PyDoc_STRVAR(doc_ctx_logical_xor,
"logical_xor($self, x, y, /)\n--\n\n\
Digit-wise xor of x and y.\n\
Applies the logical operation 'xor' between each operand's digits.\n\n\
The operands must be both logical numbers.\n\n\
>>> ExtendedContext.logical_xor(Decimal('0'), Decimal('0'))\n\
Decimal('0')\n\
>>> ExtendedContext.logical_xor(Decimal('0'), Decimal('1'))\n\
Decimal('1')\n\
>>> ExtendedContext.logical_xor(Decimal('1'), Decimal('0'))\n\
Decimal('1')\n\
>>> ExtendedContext.logical_xor(Decimal('1'), Decimal('1'))\n\
Decimal('0')\n\
>>> ExtendedContext.logical_xor(Decimal('1100'), Decimal('1010'))\n\
Decimal('110')\n\
>>> ExtendedContext.logical_xor(Decimal('1111'), Decimal('10'))\n\
Decimal('1101')\n\
>>> ExtendedContext.logical_xor(110, 1101)\n\
Decimal('1011')\n\
>>> ExtendedContext.logical_xor(Decimal(110), 1101)\n\
Decimal('1011')\n\
>>> ExtendedContext.logical_xor(110, Decimal(1101))\n\
Decimal('1011')\n\
\n");
PyDoc_STRVAR(doc_ctx_max,