# `Feel.Runtime.Number`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/runtime/number.ex#L1)

Arithmetic on FEEL numbers: decimal128, with the scale rules of DMN 1.6 (Tables
58 and 60).

A number is a `Decimal` with a coefficient of at most 34 digits and a last digit
whose exponent is at least -6176, with its first digit at exponent 6144 or lower
(see `Feel.Value.Number`). Every result here is rounded to that format, half to
even, and keeps as much scale as fits:

  * `add/2`, `subtract/2`: the scale of the operand that has more of it, so
    `.25 + .2` is `0.45`.
  * `multiply/2`: the scales added, so `.10 * 30.00` is `3.0000`.
  * `divide/2`: the exact quotient when it has at most 34 digits, with the scale
    closest to the difference of the scales that does not lose digits, so `3 / 2`
    is `1.5` and `1.01 / 2` is `0.505`; otherwise the quotient rounded to 34 digits,
    so `1 / 3` is `0.3333333333333333333333333333333333`. The specification's
    "s ≤ s1 - s2" cannot hold for `3 / 2`; this is the rule of IEEE 754 decimal128
    division, which its own examples follow (decision D-59).
  * `power/2`: one power rounded once, not a product rounded at each step. An integer
    exponent gives the exact power, with the scale of the product (and of the quotient, for
    a negative one), so `5.5 ** 2` is `30.25` and `5 ** -2` is `0.04`; any other exponent
    gives the value `exp(y * ln x)` with the zeros of its fraction removed, so `2.25 ** 0.5`
    is `1.5`.

A result outside the range of the format has no value in FEEL, and `null` takes its
place: these functions return `{:error, :overflow}`. There is no infinity and no
"not a number". A division by zero is `{:error, :division_by_zero}`, and so is a zero to a
negative power. A negative base to a power with a fraction has no real value:
`{:error, :out_of_domain}`. An operand that is not a FEEL number (too many digits, or out
of range) is `{:error, :invalid_operand}`; it can only come from outside, since no result
here is one.

A result is outside the range if its value is (D-59), after the rounding to 34 digits.
`1E-6176 * 1.0` is the coefficient 10 at -6177, and the number `1E-6176`; zeros below
-6176 are dropped, and a digit other than zero there (`1.5E-6176`, `1E-6177`, the 34
digits of `1E-6170 / 3`) is an overflow. The upper end goes by the first digit in the
same way, so `1E+6144` is a number although its coefficient is 1 at an exponent above 6111.

There is no negative zero: a result of zero is positive.

The functions use no process state and no `Decimal` context, so the precision does
not depend on who calls them. `compare/2` is exact, with no rounding.

# `error`

```elixir
@type error() :: :overflow | :division_by_zero | :invalid_operand | :out_of_domain
```

# `result`

```elixir
@type result() :: {:ok, t()} | {:error, error()}
```

# `t`

```elixir
@type t() :: Decimal.t()
```

# `add`

```elixir
@spec add(t(), t()) :: result()
```

Adds two numbers.

    iex> {:ok, sum} = Feel.Runtime.Number.add(Decimal.new("0.25"), Decimal.new("0.2"))
    iex> Decimal.to_string(sum)
    "0.45"

# `compare`

```elixir
@spec compare(t(), t()) :: :lt | :eq | :gt
```

Compares two numbers by value, exactly: `1` and `1.000` are equal, and numbers that
differ in their 34th digit are not.

    iex> Feel.Runtime.Number.compare(Decimal.new("1"), Decimal.new("1.000"))
    :eq
    iex> Feel.Runtime.Number.compare(Decimal.new("-2"), Decimal.new("1"))
    :lt

# `digit_count`

```elixir
@spec digit_count(non_neg_integer()) :: pos_integer()
```

The number of decimal digits of a non-negative integer.

The coefficient of a number has at most 34, but the integer that two numbers far apart in exponent are added in (`1E+6000 + 1`
is written at the exponent of the second) has thousands, and writing one out as text to count them cost most of the time of an
analysis of sums whose operands are not bounded (D-59). Up to 64 digits the count is found by comparing with the powers of
ten: from the smallest for an integer that is a single word (almost every one: 5 ns for it where writing it out took 20), and
by halving the range of counts for a larger one (90 ns where writing it out took 195, D-59). Above 64 digits the count is
found from the size of the integer in bits, which gives a count that is never above the right one (`log10(2)` is more than
0.30102), and then made exact against the powers of ten, one or two steps.

    iex> Feel.Runtime.Number.digit_count(0)
    1
    iex> Feel.Runtime.Number.digit_count(999)
    3
    iex> Feel.Runtime.Number.digit_count(1000)
    4
    iex> Feel.Runtime.Number.digit_count(Integer.pow(10, 6000))
    6001
    iex> Feel.Runtime.Number.digit_count(Integer.pow(10, 6000) - 1)
    6000

# `divide`

```elixir
@spec divide(t(), t()) :: result()
```

Divides the first number by the second.

    iex> {:ok, quotient} = Feel.Runtime.Number.divide(Decimal.new(3), Decimal.new(2))
    iex> Decimal.to_string(quotient)
    "1.5"
    iex> {:ok, quotient} = Feel.Runtime.Number.divide(Decimal.new(1), Decimal.new(3))
    iex> Decimal.to_string(quotient)
    "0.3333333333333333333333333333333333"
    iex> Feel.Runtime.Number.divide(Decimal.new(1), Decimal.new(0))
    {:error, :division_by_zero}

# `is_plain_number`
*macro* 

A guard for a coefficient and an exponent that make a number whatever the digits are: a coefficient of at most 34
digits and an exponent from -6176 to 6111, which is a test of integers. A number can have an exponent above 6111
and be one (`1E+6144`), and `valid?/1` says; so a clause that has this guard needs another for the rest.

# `multiply`

```elixir
@spec multiply(t(), t()) :: result()
```

Multiplies two numbers.

    iex> {:ok, product} = Feel.Runtime.Number.multiply(Decimal.new("0.10"), Decimal.new("30.00"))
    iex> Decimal.to_string(product)
    "3.0000"

# `negate`

```elixir
@spec negate(t()) :: result()
```

Negates a number. Zero stays positive.

    iex> {:ok, negated} = Feel.Runtime.Number.negate(Decimal.new("1.50"))
    iex> Decimal.to_string(negated)
    "-1.50"

# `power`

```elixir
@spec power(t(), t()) :: result()
```

Raises the first number to the power of the second, as one correctly rounded result and not
as repeated multiplication (see `Feel.Runtime.Number.Power`).

    iex> {:ok, power} = Feel.Runtime.Number.power(Decimal.new("5.5"), Decimal.new(2))
    iex> Decimal.to_string(power, :normal)
    "30.25"
    iex> {:ok, power} = Feel.Runtime.Number.power(Decimal.new(5), Decimal.new(-2))
    iex> Decimal.to_string(power, :normal)
    "0.04"
    iex> Feel.Runtime.Number.power(Decimal.new(0), Decimal.new(-1))
    {:error, :division_by_zero}
    iex> Feel.Runtime.Number.power(Decimal.new(-2), Decimal.new("0.5"))
    {:error, :out_of_domain}

# `subtract`

```elixir
@spec subtract(t(), t()) :: result()
```

Subtracts the second number from the first.

    iex> {:ok, difference} = Feel.Runtime.Number.subtract(Decimal.new("1.50"), Decimal.new("1.5"))
    iex> Decimal.to_string(difference)
    "0.00"

# `valid?`

```elixir
@spec valid?(term()) :: boolean()
```

Returns `true` for a finite `Decimal` that is a FEEL number: a coefficient of at
most 34 digits, a last digit not below exponent -6176, a first digit not above
exponent 6144.

    iex> Feel.Runtime.Number.valid?(Decimal.new("1.5"))
    true
    iex> Feel.Runtime.Number.valid?(Decimal.new(1, 1, 6145))
    false
    iex> Feel.Runtime.Number.valid?(Decimal.new("NaN"))
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
