cdecimal

The cdecimal module is compatible with decimal.py. Since the official documentation is valid, the rest of this document focuses on the few remaining differences as well as extra features.

Setting Context Values

  • prec, Emin, Emax, rounding, capitals and clamp are implemented as getters/setters.

  • An additional field _allcr toggles correct rounding for exp, ln and log10. If enabled (default), all methods except power and invroot are correctly rounded.

  • traps and flags have the custom type SignalDict, which behaves like a dictionary for most purposes. This is the familiar interface from decimal.py.

  • Internally, traps and flags are just C unsigned integers. cdecimal provides the option to access the integers directly using the getters/setters _traps and _flags.

  • The use of the two interfaces can be mixed freely. The following table shows how the SignalDict items and the C-flags are related:

    SignalDict C signals C conditions
    InvalidOperation DecIEEEInvalidOperation DecConversionSyntax
    DecDivisionImpossible
    DecDivisionUndefined
    DecInvalidContext
    DecInvalidOperation
    DecMallocError
    Clamped DecClamped DecClamped
    DivisionByZero DecDivisionByZero DecDivisionByZero
    FloatOperation [1] DecFloatOperation DecFloatOperation
    Inexact DecInexact DecInexact
    Rounded DecRounded DecRounded
    Subnormal DecSubnormal DecSubnormal
    Overflow DecOverflow DecOverflow
    Underflow DecUnderflow DecUnderflow
    [1]

    cdecimal extension.

  • An example of mixing the two interfaces:

    >>> from cdecimal import *
    >>> c = getcontext()               # Global (thread-local) context
    >>>
    >>> c.traps[Inexact] = True        # Trap the Inexact signal
    >>> c.traps[Inexact] = False       # Clear the Inexact signal
    >>>
    >>> c._traps |= DecInexact         # Trap the Inexact signal
    >>> c.traps[Inexact]
    True
    >>> c._traps &= ~DecInexact        # Clear the Inexact signal
    >>> c.traps[Inexact]
    False
    

Context Limits

Module constants:

  cdecimal 32-bit cdecimal 64-bit
MAX_PREC 425000000 999999999999999999
MAX_EMAX 425000000 999999999999999999
MIN_EMIN -425000000 -999999999999999999

Valid ranges:

  decimal.py cdecimal
prec [1, unlimited] [1, MPD_MAX_PREC]
Emax [0, unlimited] [0, MPD_MAX_EMAX]
Emin [-unlimited, 0] [MPD_MIN_EMIN, 0]

Thread Local Default Context

The choice whether the default context is thread local or global is made at compile time. If thread local contexts are enabled, the module constant HAVE_THREADS has a value of 1. Otherwise, the value is 0.

By default, local contexts are enabled.

Decimal Constructor

The Decimal constructor does not observe the current context, i.e. any value is read exactly as entered. Since the context of cdecimal has limits, the following approach is used:

If an Inexact or Rounded condition occurs during conversion, InvalidOperation is raised and the result is NaN. In this case, the create_decimal context method has to be used.

Power Method

The power method in decimal.py is correctly rounded. cdecimal currently only guarantees an error less than 1ULP+t, where t has a maximum of 0.1ULP, but is almost always less than 0.01ULP.

FloatOperation Signal

In Python 2.7 and 3.2 the rules for mixing decimals and floats were relaxed. This can give rise to mistakes:

>>> Decimal(1.1)    # meaning Decimal("1.1")
Decimal('1.100000000000000088817841970012523233890533447265625')
>>> x = Decimal("1.9")
>>> x > 1.9         # meaning Decimal(1.9)
True

In order to prevent accidental mixing, cdecimal has an additional FloatOperation signal. Here is the behavior with the trap set:

>>> c = getcontext()
>>> c.traps[FloatOperation] = True
>>> Decimal(1.9)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
cdecimal.FloatOperation: [<class 'cdecimal.FloatOperation'>]
>>>
>>> x = Decimal("1.9")
>>> x > 1.9
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
cdecimal.FloatOperation: [<class 'cdecimal.FloatOperation'>]
>>>

For compatibility with decimal.py the FloatOperation signal is off by default. This is the standard behavior for mixed operations across several Python versions:

  2.5 2.6 2.7 3.1 3.2
mixed arithmetic TypeError TypeError TypeError TypeError TypeError
constructor TypeError TypeError exact [4] TypeError exact
__eq__ false [2] false exact false exact
__ne__ true [3] true exact true exact
ordering arbitrary arbitrary exact TypeError exact

The same table with the FloatOperation signal enabled:

  2.5 2.6 2.7 3.1 3.2
mixed arithmetic TypeError TypeError TypeError TypeError TypeError
constructor TypeError TypeError FloatOp TypeError FloatOp
__eq__ false false exact false exact
__ne__ true true exact true exact
ordering FloatOp [5] FloatOp FloatOp FloatOp FloatOp
[2]always false
[3]always true
[4]exact conversions or comparisons
[5]raises FloatOperation

IEEEContext Function

cdecimal.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.

For the most common values, the following constants are provided:

  DECIMAL32 DECIMAL64 DECIMAL128
prec 7 16 34
emax 96 384 6144
emin -95 -383 -6143
round ROUND_HALF_EVEN ROUND_HALF_EVEN ROUND_HALF_EVEN
traps 0 0 0
status 0 0 0
newtrap 0 0 0
clamp 1 1 1
allcr 1 1 1