# Context

Most operations are affected by context. Usually, a pointer to the context is passed as the last parameter to signaling functions or the last but one parameter to quiet functions.

## Data Type

The context is defined as a C struct:

    #include <mpdecimal.h>

    typedef struct mpd_context_t {
            mpd_ssize_t prec;   // precision
            mpd_ssize_t emax;   // max positive exp
            mpd_ssize_t emin;   // min negative exp
            uint32_t traps;     // status events that should be trapped
            uint32_t status;    // status flags
            uint32_t newtrap;   // set by mpd_addstatus_raise()
            int      round;     // rounding mode
            int      clamp;     // clamp mode
            int      allcr;     // all functions correctly rounded
    } mpd_context_t;

## Constants

> | 32-bit | 64-bit
> --- | --- | ---
> `MPD_MAX_PREC` | `425000000` | `999999999999999999`
> `MPD_MAX_EMAX` | `425000000` | `999999999999999999`
> `MPD_MIN_EMIN` | `-425000000` | `-999999999999999999`

## Precision and Exponents

> prec | [`1`, `MPD_MAX_PREC`]
> emax | [`0`, `MPD_MAX_EMAX`]
> emin | [`MPD_MIN_EMIN`, `0`]

Apart from the limits, the following rules must be observed when setting the context manually:

> - prec <= emax
> - emin = 1 - emax or emin = -emax

The specification requires 5 x prec <= emax and recommends 10 x prec <= emax.

## Rounding

The round field can take one of these values:

> `MPD_ROUND_UP` | round away from `0`
> `MPD_ROUND_DOWN` | round toward `0` (truncate)
> `MPD_ROUND_CEILING` | round toward `+infinity`
> `MPD_ROUND_FLOOR` | round toward `-infinity`
> `MPD_ROUND_HALF_UP` | `0.5` is rounded up
> `MPD_ROUND_HALF_DOWN` | `0.5` is rounded down
> `MPD_ROUND_HALF_EVEN` | `0.5` is rounded to even
> `MPD_ROUND_05UP` | round zero or five away from `0`
> `MPD_ROUND_TRUNC` | truncate, but set infinities

## Signals and Conditions

The standard distinguishes between signals and conditions. In particular, the `MPD_IEEE_Invalid_operation` signal comprises several conditions.

In a signaling function, the status field of the context is updated with all conditions that occurred during the execution. It is never reset automatically, so at any point in a program it contains the cumulative status of all signaling functions.

The traps field determines which signals invoke the `mpd_traphandler` custom function. By default, this function raises `SIGFPE`.

If a trap occurs, the newtrap field is set to the value of the respective condition. This makes it possible to determine the latest error condition that occurred even when the status has already been “polluted” with previous error conditions.

Here are the possible signals and conditions:

> Signals | Conditions
> --- | ---
> `MPD_IEEE_Invalid_operation` | `MPD_Conversion_syntax`
> `MPD_Division_impossible`
> `MPD_Division_undefined`
> `MPD_Invalid_context`
> `MPD_Invalid_operation`
> `MPD_Malloc_error`
> `MPD_Clamped` | `MPD_Clamped`
> `MPD_Division_by_zero` | `MPD_Division_by_zero`
> `MPD_Fpu_error` [1] | `MPD_Fpu_error`
> `MPD_Inexact` | `MPD_Inexact`
> `MPD_Not_implemented` [2] | `MPD_Not_implemented`
> `MPD_Overflow` | `MPD_Overflow`
> `MPD_Rounded` | `MPD_Rounded`
> `MPD_Subnormal` | `MPD_Subnormal`
> `MPD_Underflow` | `MPD_Underflow`

> [1] | deprecated, might be renamed to a user-definable signal.

> [2] | unused, might be renamed to a user-definable signal.

## Exponent Clamping

If the `clamp` field is set to 1, the maximum exponent is reduced to emax - prec + 1. This is compatible with the IEEE 754 decimal interchange formats.

## Correct Rounding

Most functions are correctly-rounded by default. If allcr is set to 1, correct rounding is additionally enabled for `mpd_exp`, `mpd_ln` and `mpd_log10`.

In this case, all functions except `mpd_pow` and `mpd_invroot` return correctly rounded results.

# Context Functions

## Initialization

    void mpd_init(mpd_context_t *ctx, mpd_ssize_t prec);

ctx is initialized by `mpd_defaultcontext`, using prec. At the same time, `MPD_MINALLOC` is initialized to the ideal value for the given precision. Note that memory usage increases by setting `MPD_MINALLOC` to higher values.

This function can only be used at program start.

    void mpd_maxcontext(mpd_context_t *ctx);
    void mpd_defaultcontext(mpd_context_t *ctx);
    void mpd_basiccontext(mpd_context_t *ctx);

Initialize the given context. The values for each function:

| maxcontext | defaultcontext [3] | basiccontext [4]
--- | --- | --- | ---
prec | `MPD_MAX_PREC` | `2*MPD_RDIGITS` | `9`
emax | `MPD_MAX_EMAX` | `MPD_MAX_EMAX` | `MPD_MAX_EMAX`
emin | `MPD_MIN_EMIN` | `MPD_MIN_EMIN` | `MPD_MIN_EMIN`
round | `MPD_ROUND_HALF_EVEN` | `MPD_ROUND_HALF_UP` | `MPD_ROUND_HALF_UP`
traps | `MPD_Traps` | `MPD_Traps` | `MPD_Traps|MPD_Clamped`
status | `0` | `0` | `0`
newtrap | `0` | `0` | `0`
clamp | `0` | `0` | `0`
allcr | `1` | `1` | `1`

[3] | libmpdec’s default context

[4] | the specification’s basic default context

## IEEE Interchange Formats

    MPD_IEEE_CONTEXT_MAX_BITS
    MPD_DECIMAL32
    MPD_DECIMAL64
    MPD_DECIMAL128

    int mpd_ieee_context(mpd_context_t *ctx, int bits);

Initialize the context 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:

| `MPD_DECIMAL32` | `MPD_DECIMAL64` | `MPD_DECIMAL128`
--- | --- | --- | ---
prec | `7` | `16` | `34`
emax | `96` | `384` | `6144`
emin | `-95` | `-383` | `-6143`
round | `MPD_ROUND_HALF_EVEN` | `MPD_ROUND_HALF_EVEN` | `MPD_ROUND_HALF_EVEN`
traps | `0` | `0` | `0`
status | `0` | `0` | `0`
newtrap | `0` | `0` | `0`
clamp | `1` | `1` | `1`
allcr | `1` | `1` | `1`

## Getters and Setters

    size_t mpd_getprec(const mpd_context_t *ctx);
    mpd_ssize_t mpd_getemax(const mpd_context_t *ctx);
    mpd_ssize_t mpd_getemin(const mpd_context_t *ctx);
    int mpd_getround(const mpd_context_t *ctx);
    uint32_t mpd_gettraps(const mpd_context_t *ctx);
    uint32_t mpd_getstatus(const mpd_context_t *ctx);
    int mpd_getclamp(const mpd_context_t *ctx);
    int mpd_getcr(const mpd_context_t *ctx);

Get the individual values.

    mpd_ssize_t mpd_etiny(const mpd_context_t *ctx);

Return the lowest possible exponent of a subnormal number: emin - prec + 1

    mpd_ssize_t mpd_etop(const mpd_context_t *ctx);

Return the highest possible exponent of a normal number: emax - prec + 1

Only relevant if clamp is set to 1.

    int mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec);
    int mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax);
    int mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin);
    int mpd_qsetround(mpd_context_t *ctx, int newround);
    int mpd_qsettraps(mpd_context_t *ctx, uint32_t flags);
    int mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags);
    int mpd_qsetclamp(mpd_context_t *ctx, int c);
    int mpd_qsetcr(mpd_context_t *ctx, int c);

Quietly set the individual values. These functions check the limits, but they are not foolproof: For example, they still allow setting a prec that does not agree with emax. The functions return 1 on success and 0 on failure. They are quiet since raising an `MPD_Invalid_context` condition would not make sense in most cases.

## Calling the Trap Handler

    extern void (* mpd_traphandler)(mpd_context_t *);
    void mpd_dflt_traphandler(mpd_context_t *);

    void mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags);

Add flags to the status field of ctx. If a condition is trapped, set newtrap to the condition and call `mpd_traphandler`. By default, `mpd_traphandler` is points to `mpd_dflt_traphandler`, which raises `SIGFPE`.