Feel.Runtime.Number.Power (feel v0.1.0)

Copy Markdown View Source

The power base ** exponent of two FEEL numbers, as one correctly rounded result (DMN 1.6, decision D-62).

A power is not a repeated multiplication with a rounding at each step: the example of the specification (the payment PMT, section 10.6) comes out right only if the exact power is rounded once, to 34 digits, half to even. Feel.Runtime.Number.power/2 checks the operands and calls power/2 here, which assumes they are numbers.

An integer exponent (its value is, so 2, 2.0 and 1E+1 are; it may be negative) takes the exact power of the base and rounds that. The scale follows from the exactness, as it does for a product: 5 ** 2 is 25, 5.5 ** 2 is 30.25, 5.50 ** 2 is 30.2500, 10 ** 5 is 100000. A negative exponent divides one by the exact power with the scale rule of Feel.Runtime.Number.divide/2: the exact quotient when it has at most 34 digits, otherwise the quotient rounded, so 5 ** -2 is 0.04 and 10 ** -5 is 0.00001. A negative base has the sign of the parity of the exponent.

Any other exponent is exp(y * ln x), which has a real value only for a positive base, so a negative base is {:error, :out_of_domain}. It is computed in integer arithmetic with fixed-point numbers of more than 130 decimal digits, which is far more than the 34 digits of the result, and the result is rounded from that. The working precision is raised, until the rounding is certain, if the digits after the 34th are too near to a tie for the error of the computation to decide. The value is irrational unless the root is exact, so the coefficient of an exact result such as 4 ** 0.5 has its trailing zeros removed down to the scale of an integer: 2, 1.5 for 2.25 ** 0.5, and 10 for 100 ** 0.5.

The special cases. Any base to the power zero is 1, zero included. Zero to a positive power is zero, with the scale of the product for an integer exponent (0.0 ** 2 is 0.00), and zero to a negative power is {:error, :division_by_zero}. One to a fractional power is 1.

The range. A result outside the format is {:error, :overflow}, as for the other operations, and so is a nonzero result too small to write (D-59): 10 ** 6145, 0.1 ** 6177. A power of ten is worked out from its exponent alone, the other results by an exact integer power while that has at most 10,000 digits and by logarithms beyond, so that no exponent makes the call slow or large: 7 ** 99999999999, 1E+6000 ** 1000000 and 1.000 ** 1000000000 return at once.

There is no process state and no Decimal context, and the floating point numbers that appear are only the estimate of a size, never a digit of a result.

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(10), Decimal.new(-5))
iex> Decimal.to_string(power, :normal)
"0.00001"
iex> {:ok, power} = Feel.Runtime.Number.power(Decimal.new(2), Decimal.new("0.5"))
iex> Decimal.to_string(power, :normal)
"1.414213562373095048801688724209698"
iex> {:ok, power} = Feel.Runtime.Number.power(Decimal.new("2.25"), Decimal.new("0.5"))
iex> Decimal.to_string(power, :normal)
"1.5"
iex> Feel.Runtime.Number.power(Decimal.new(-4), Decimal.new("0.5"))
{:error, :out_of_domain}
iex> Feel.Runtime.Number.power(Decimal.new(10), Decimal.new(6145))
{:error, :overflow}

Summary

Functions

e raised to a number, rounded to 34 digits, half to even.

The natural logarithm of a positive number, rounded to 34 digits, half to even.

Raises base to exponent. Both must be FEEL numbers.

Functions

exp(decimal)

e raised to a number, rounded to 34 digits, half to even.

A result outside the format is {:error, :overflow}, as for the other operations, and so is one too small to write.

iex> {:ok, e} = Feel.Runtime.Number.Power.exp(Decimal.new(1))
iex> Decimal.to_string(e, :normal)
"2.718281828459045235360287471352662"
iex> {:ok, e} = Feel.Runtime.Number.Power.exp(Decimal.new(5))
iex> Decimal.to_string(e, :normal)
"148.4131591025766034211155800405523"
iex> {:ok, one} = Feel.Runtime.Number.Power.exp(Decimal.new("0.0"))
iex> Decimal.to_string(one, :normal)
"1"
iex> Feel.Runtime.Number.Power.exp(Decimal.new(100000))
{:error, :overflow}

ln(decimal)

The natural logarithm of a positive number, rounded to 34 digits, half to even.

Zero and the negative numbers have no logarithm: {:error, :out_of_domain}.

iex> {:ok, log} = Feel.Runtime.Number.Power.ln(Decimal.new(10))
iex> Decimal.to_string(log, :normal)
"2.302585092994045684017991454684364"
iex> {:ok, log} = Feel.Runtime.Number.Power.ln(Decimal.new("1.000"))
iex> Decimal.to_string(log, :normal)
"0"
iex> Feel.Runtime.Number.Power.ln(Decimal.new(0))
{:error, :out_of_domain}

power(base, exponent)

Raises base to exponent. Both must be FEEL numbers.

iex> {:ok, power} = Feel.Runtime.Number.Power.power(Decimal.new(5), Decimal.new(-2))
iex> Decimal.to_string(power, :normal)
"0.04"