Skip to main content

_pydecimal.py: Pure-Python Decimal Arithmetic

_pydecimal.py is the pure-Python reference implementation of the decimal module. CPython normally loads the C accelerator _decimal, but the pure-Python file is the authoritative spec for behavior and the fallback on platforms without the C extension.

Map

LinesSymbolPurpose
1–120module header, __all__Imports, version note, public API list
121–310DefaultContext, signal classesSentinel objects for the 15 trap signals
311–600ContextThread-local rounding mode, precision, traps
601–900_convert_otherCoerce int/float/str to Decimal before ops
901–1400Decimal.__new__, _int_to_stringDigit-tuple storage, coefficient array
1401–2200Decimal arithmetic methods__add__, __mul__, __divmod__, __pow__
2201–2700Rounding helpers_round_half_even, _round_ceiling, etc.
2701–3200Decimal comparison and conversion__eq__, __lt__, to_eng_string
3201–3800Decimal special methodssqrt, ln, log10, exp
3801–4400Context arithmetic dispatchMirrors Decimal methods, applies context
4401–5200localcontext, setcontextThread-local context management
5201–6000_ContextManager, named contextsBasicContext, ExtendedContext, tests

Reading

Digit-array representation

A Decimal stores its coefficient as a tuple of integer digits in _int and an integer exponent in _exp. The sign is a separate _sign field (0 or 1). Special values (Infinity, NaN, sNaN) are encoded via _is_special. This mirrors the General Decimal Arithmetic specification layout directly.

Context and rounding modes

Context holds prec (precision), rounding (one of the ROUND_* constants), and a dict of trap flags. When an operation produces an inexact result, it calls context._raise_error(signal). If the trap is set, a Python exception is raised. If not, the signal flag is recorded silently and a default value is returned. The nine rounding modes (ROUND_HALF_EVEN, ROUND_UP, ROUND_FLOOR, etc.) are each implemented as a private _round_* function and dispatched by name from a _rounding_modes dict.

_convert_other and arithmetic dispatch

Every binary operator calls _convert_other(other) first. The function returns NotImplemented for unrecognized types, allowing Python's reflected-operator protocol to take over. For int inputs it calls Decimal(other) directly. Arithmetic then proceeds via coefficient manipulation under the active context's precision, rounding, and trap settings.

gopy notes

  • The C extension _decimal wraps libmpdec. The pure-Python file is the port target for gopy because it expresses every algorithm in auditable Python.
  • Signal classes (InvalidOperation, DivisionByZero, etc.) map to Go error sentinel values in the port.
  • localcontext() requires goroutine-local storage. In gopy this is implemented via vm.Frame context chaining rather than threading.local.
  • 3.14 added Decimal.from_number as a cleaner alternative to Decimal(float). The _convert_other path was updated to call it.