Skip to content

dpmxl.values

Values and their comparison. See Interval arithmetic.

dpmxl.values

Runtime values: numbers with interval thresholds, items, text, dates.

Numbers follow the Eurofiling interval arithmetic functions (iaf:*) that EBA validation rules are based on: a number is a value with a symmetric threshold, so it stands for the interval [value - t, value + t]. Facts reported with XBRL decimals get t = 0.5 * 10**-decimals (INF is exact); constants and defaults are exact. Sums and differences add thresholds, products use |A*dB| + |B*dA| + dA*dB, quotients take the largest deviation of the endpoint quotients, abs and negation keep the threshold, and min/max keep the chosen value's threshold. Two numbers are equal when |a - b| <= ta + tb, i.e. when their intervals overlap. Selections evaluated with interval: false drop the threshold, so their comparisons are exact.

Arithmetic uses DECIMAL_CONTEXT (34 significant digits, as IEEE decimal128), whatever the caller's thread decimal context.

DECIMAL_CONTEXT module-attribute

DECIMAL_CONTEXT = Context(
    prec=34,
    rounding=ROUND_HALF_EVEN,
    Emax=6144,
    Emin=-6143,
    traps=[DivisionByZero, InvalidOperation, Overflow],
)

The decimal context of every operation on numbers (IEEE decimal128).

Value module-attribute

Value: TypeAlias = (
    Number | Item | str | bool | date | ValueSet | None
)

A runtime value: Number, Item, text, Boolean, date (or date-time), ValueSet or None (null).

Number dataclass

Number(value: Decimal, tolerance: Decimal = ZERO)

A decimal value with a symmetric tolerance for interval arithmetic.

A number stands for the interval [value - tolerance, value + tolerance]. Build reported facts with Number.of, which derives the tolerance from XBRL decimals. Arithmetic operators (+, -, *, /, unary - and abs) follow the interval rules in the module documentation.

Attributes:

  • value (Decimal) –

    The amount.

  • tolerance (Decimal) –

    Half-width of the interval; 0 for an exact number.

Examples:

>>> Number.of(2, 0) + Number.of(3, 1)
Number(value=Decimal('5'), tolerance=Decimal('0.55'))

value instance-attribute

value: Decimal

tolerance class-attribute instance-attribute

tolerance: Decimal = ZERO

low property

low: Decimal

Lower interval bound, value - tolerance.

Examples:

>>> Number.of(1204, 0).low, Number.of(1204, 0).high
(Decimal('1203.5'), Decimal('1204.5'))

high property

high: Decimal

Upper interval bound, value + tolerance.

of classmethod

of(
    value: int | str | Decimal,
    decimals: int | str | None = None,
) -> Number

Build a number, deriving tolerance from XBRL decimals.

Parameters:

  • value (int | str | Decimal) –

    The amount.

  • decimals (int | str | None, default: None ) –

    XBRL decimals: an integer, "INF" (in any case) or None, which both mean exact.

Returns:

  • Number ( Number ) –

    The value with 0.5 * 10**-decimals tolerance, or an

  • Number –

    exact value.

Raises:

  • ValueError –

    If decimals is text other than INF or an integer, or puts the tolerance outside the range of DECIMAL_CONTEXT (beyond about ±6,100).

Examples:

>>> Number.of(1204, 0)
Number(value=Decimal('1204'), tolerance=Decimal('0.5'))
>>> Number.of("250.4", 1).tolerance, Number.of(1000, -3).tolerance
(Decimal('0.05'), Decimal('5E+2'))
>>> Number.of(5, "INF").tolerance
Decimal('0')

exact

exact() -> Number

Return the same value without tolerance.

Returns:

  • Number ( Number ) –

    An exact copy.

__add__

__add__(other: Number) -> Number

Add values and tolerances.

Returns:

  • Number ( Number ) –

    The result.

__sub__

__sub__(other: Number) -> Number

Subtract values; tolerances add up.

Returns:

  • Number ( Number ) –

    The result.

__mul__

__mul__(other: Number) -> Number

Multiply as Eurofiling's iaf:numeric-multiply does.

The threshold is |A * dB| + |B * dA| + dA * dB.

Returns:

  • Number ( Number ) –

    The result.

__truediv__

__truediv__(other: Number) -> Number

Divide as Eurofiling's iaf:numeric-divide does.

The threshold is the largest deviation of the four endpoint quotients (A ± dA) / (B ± dB) from A / B. An endpoint divisor of zero is skipped rather than making the result unbounded.

Returns:

  • Number ( Number ) –

    The quotient.

Raises:

  • ZeroDivisionError –

    If other is zero.

__neg__

__neg__() -> Number

Negate the value.

Returns:

  • Number ( Number ) –

    The result.

__abs__

__abs__() -> Number

Return the absolute value.

Returns:

  • Number ( Number ) –

    The result.

ValueSet dataclass

ValueSet(members: tuple[Value, ...] = ())

A set of distinct scalar values.

Set literals, set-* parameters and set operators yield sets. Members are kept in first-seen order; numbers are exact, so 1 and 1.0 are one member. Build sets with ValueSet.of, which removes duplicates by DPM-XL equality.

members class-attribute instance-attribute

members: tuple[Value, ...] = ()

The members in first-seen order.

of classmethod

of(values: Iterable[Value]) -> ValueSet

Collect distinct non-null values.

Returns:

  • ValueSet ( ValueSet ) –

    The set, in first-seen order.

Raises:

  • TypeError –

    If a value is itself a set.

Examples:

>>> values = ValueSet.of([Number.of(1), Number.of("1.0"), None, "a"])
>>> len(values), Number.of(1) in values, "b" in values
(2, True, False)

__contains__

__contains__(value: object) -> bool

Whether a member equals value (numbers by interval overlap).

Returns:

  • bool ( bool ) –

    Membership.

__len__

__len__() -> int

Return the number of members.

Returns:

  • int ( int ) –

    The size.

compare

compare(op: str, left: Value, right: Value) -> bool | None

Compare two scalar values with DPM-XL semantics.

Numbers use interval comparison: a = b holds when the tolerance intervals overlap. Items compare by category and code, ignoring a missing category. Null on either side yields null.

Returns:

  • bool | None –

    bool | None: The comparison result, or None for null operands.

Raises:

  • TypeError –

    When the operands cannot be compared.

  • ValueError –

    When op is not a comparison operator.

Examples:

>>> compare("=", Number.of(1204, 0), Number.of(1205, 0))  # overlap
True
>>> compare("=", Number.of(1204, 0), Number.of(1206, 0))
False
>>> compare("<", None, Number.of(1)) is None
True
>>> compare("=", Item.parse("eba_qEC:qx01"), Item.parse("qx01"))
True

type_name

type_name(value: Value) -> str

Return a readable type name for error messages.

Returns:

  • str ( str ) –

    For example number or item.

Examples:

>>> type_name(Number.of(1)), type_name("x"), type_name(None)
('number', 'string', 'null')

format_value

format_value(value: Value) -> str

Format a value for an explanation.

Numbers use thousands separators and drop trailing zeros; items keep their category:code form in brackets.

Returns:

  • str ( str ) –

    Human-readable text.

Examples:

>>> from datetime import date
>>> format_value(Number.of("1234567.50")), format_value(Item.parse("a:b"))
('1,234,567.5', '[a:b]')
>>> format_value(date(2026, 12, 31)), format_value(True), format_value(None)
('2026-12-31', 'true', 'null')
>>> format_value("text")
'"text"'

format_decimal

format_decimal(value: Decimal) -> str

Format a decimal with thousands separators and no exponent.

Returns:

  • str ( str ) –

    For example 1,204 or -0.075.

Examples:

>>> format_decimal(Decimal("1234567.8900"))
'1,234,567.89'

format_computed

format_computed(value: Decimal) -> str

Format a computed decimal, rounding a long fraction for display.

A division yields up to 34 significant digits. Beyond ten decimals, the number shows six significant digits, but keeps every integer digit and two decimals: 1.852935352358765... reads 1.85294 and 12345.67890123456 12,345.68. Shorter fractions, such as the exact difference of two reported values, are shown in full, as reported values always are.

Returns:

  • str ( str ) –

    The formatted number.

Examples:

>>> format_computed(Decimal("1.852935352358765123"))
'1.85294'
>>> format_computed(Decimal("12345.67890123456"))
'12,345.68'