diff options
author | Sergey B Kirpichev <skirpichev@gmail.com> | 2025-04-28 16:05:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-28 15:05:56 +0200 |
commit | 5bf0f3666e272798789ff900b1071760c73b46fd (patch) | |
tree | a4a60b0cfa75ee1181b77bee4a1749dcb5e748fd /Lib | |
parent | e20ca6d1b006674be23d16083f273e8a7b8f77b6 (diff) | |
download | cpython-5bf0f3666e272798789ff900b1071760c73b46fd.tar.gz cpython-5bf0f3666e272798789ff900b1071760c73b46fd.zip |
gh-53032: support IEEE 754 contexts in the decimal module (#122003)
This was in C version from beginning, but available only
on conditional compilation (EXTRA_FUNCTIONALITY). Current
patch adds function to create IEEE contexts to the
pure-python module as well.
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/_pydecimal.py | 27 | ||||
-rw-r--r-- | Lib/test/test_decimal.py | 88 |
2 files changed, 71 insertions, 44 deletions
diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index d666c4133c3..4b09207eca6 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -38,10 +38,10 @@ __all__ = [ 'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', 'ROUND_05UP', # Functions for manipulating contexts - 'setcontext', 'getcontext', 'localcontext', + 'setcontext', 'getcontext', 'localcontext', 'IEEEContext', # Limits for the C version for compatibility - 'MAX_PREC', 'MAX_EMAX', 'MIN_EMIN', 'MIN_ETINY', + 'MAX_PREC', 'MAX_EMAX', 'MIN_EMIN', 'MIN_ETINY', 'IEEE_CONTEXT_MAX_BITS', # C version: compile time choice that enables the thread local context (deprecated, now always true) 'HAVE_THREADS', @@ -83,10 +83,12 @@ if sys.maxsize == 2**63-1: MAX_PREC = 999999999999999999 MAX_EMAX = 999999999999999999 MIN_EMIN = -999999999999999999 + IEEE_CONTEXT_MAX_BITS = 512 else: MAX_PREC = 425000000 MAX_EMAX = 425000000 MIN_EMIN = -425000000 + IEEE_CONTEXT_MAX_BITS = 256 MIN_ETINY = MIN_EMIN - (MAX_PREC-1) @@ -417,6 +419,27 @@ def localcontext(ctx=None, **kwargs): return ctx_manager +def IEEEContext(bits, /): + """ + Return a context object initialized to the proper values for one of the + IEEE interchange formats. The argument must be a multiple of 32 and less + than IEEE_CONTEXT_MAX_BITS. + """ + if bits <= 0 or bits > IEEE_CONTEXT_MAX_BITS or bits % 32: + raise ValueError("argument must be a multiple of 32, " + f"with a maximum of {IEEE_CONTEXT_MAX_BITS}") + + ctx = Context() + ctx.prec = 9 * (bits//32) - 2 + ctx.Emax = 3 * (1 << (bits//16 + 3)) + ctx.Emin = 1 - ctx.Emax + ctx.rounding = ROUND_HALF_EVEN + ctx.clamp = 1 + ctx.traps = dict.fromkeys(_signals, False) + + return ctx + + ##### Decimal class ####################################################### # Do not subclass Decimal from numbers.Real and do not register it as such diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 92dafc56dc2..9e298401dc3 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -4399,6 +4399,51 @@ class CContextSubclassing(ContextSubclassing, unittest.TestCase): class PyContextSubclassing(ContextSubclassing, unittest.TestCase): decimal = P +class IEEEContexts: + + def test_ieee_context(self): + # issue 8786: Add support for IEEE 754 contexts to decimal module. + IEEEContext = self.decimal.IEEEContext + + def assert_rest(self, context): + self.assertEqual(context.clamp, 1) + assert_signals(self, context, 'traps', []) + assert_signals(self, context, 'flags', []) + + c = IEEEContext(32) + self.assertEqual(c.prec, 7) + self.assertEqual(c.Emax, 96) + self.assertEqual(c.Emin, -95) + assert_rest(self, c) + + c = IEEEContext(64) + self.assertEqual(c.prec, 16) + self.assertEqual(c.Emax, 384) + self.assertEqual(c.Emin, -383) + assert_rest(self, c) + + c = IEEEContext(128) + self.assertEqual(c.prec, 34) + self.assertEqual(c.Emax, 6144) + self.assertEqual(c.Emin, -6143) + assert_rest(self, c) + + # Invalid values + self.assertRaises(ValueError, IEEEContext, -1) + self.assertRaises(ValueError, IEEEContext, 123) + self.assertRaises(ValueError, IEEEContext, 1024) + + def test_constants(self): + # IEEEContext + IEEE_CONTEXT_MAX_BITS = self.decimal.IEEE_CONTEXT_MAX_BITS + self.assertIn(IEEE_CONTEXT_MAX_BITS, {256, 512}) + +@requires_cdecimal +class CIEEEContexts(IEEEContexts, unittest.TestCase): + decimal = C +class PyIEEEContexts(IEEEContexts, unittest.TestCase): + decimal = P + @skip_if_extra_functionality @requires_cdecimal class CheckAttributes(unittest.TestCase): @@ -4410,6 +4455,7 @@ class CheckAttributes(unittest.TestCase): self.assertEqual(C.MAX_EMAX, P.MAX_EMAX) self.assertEqual(C.MIN_EMIN, P.MIN_EMIN) self.assertEqual(C.MIN_ETINY, P.MIN_ETINY) + self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, P.IEEE_CONTEXT_MAX_BITS) self.assertTrue(C.HAVE_THREADS is True or C.HAVE_THREADS is False) self.assertTrue(P.HAVE_THREADS is True or P.HAVE_THREADS is False) @@ -4894,42 +4940,6 @@ class CFunctionality(unittest.TestCase): """Extra functionality in _decimal""" @requires_extra_functionality - def test_c_ieee_context(self): - # issue 8786: Add support for IEEE 754 contexts to decimal module. - IEEEContext = C.IEEEContext - DECIMAL32 = C.DECIMAL32 - DECIMAL64 = C.DECIMAL64 - DECIMAL128 = C.DECIMAL128 - - def assert_rest(self, context): - self.assertEqual(context.clamp, 1) - assert_signals(self, context, 'traps', []) - assert_signals(self, context, 'flags', []) - - c = IEEEContext(DECIMAL32) - self.assertEqual(c.prec, 7) - self.assertEqual(c.Emax, 96) - self.assertEqual(c.Emin, -95) - assert_rest(self, c) - - c = IEEEContext(DECIMAL64) - self.assertEqual(c.prec, 16) - self.assertEqual(c.Emax, 384) - self.assertEqual(c.Emin, -383) - assert_rest(self, c) - - c = IEEEContext(DECIMAL128) - self.assertEqual(c.prec, 34) - self.assertEqual(c.Emax, 6144) - self.assertEqual(c.Emin, -6143) - assert_rest(self, c) - - # Invalid values - self.assertRaises(OverflowError, IEEEContext, 2**63) - self.assertRaises(ValueError, IEEEContext, -1) - self.assertRaises(ValueError, IEEEContext, 1024) - - @requires_extra_functionality def test_c_context(self): Context = C.Context @@ -4949,12 +4959,6 @@ class CFunctionality(unittest.TestCase): C.DecSubnormal, C.DecUnderflow ) - # IEEEContext - self.assertEqual(C.DECIMAL32, 32) - self.assertEqual(C.DECIMAL64, 64) - self.assertEqual(C.DECIMAL128, 128) - self.assertEqual(C.IEEE_CONTEXT_MAX_BITS, 512) - # Conditions for i, v in enumerate(cond): self.assertEqual(v, 1<<i) |