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

  • 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.29299)    # meaning Decimal("1.29299")
Decimal('1.292990000000000083701934272539801895618438720703125')
>>> 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

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