Feel.Type.ExactNumber (feel v0.1.0)

Copy Markdown View Source

Numbers worked on without rounding (D-109).

A Decimal is a whole number over a power of ten, and the arithmetic of the Decimal library rounds to the precision of a context (28 digits). The analysis of the amounts of a duration and of the days of a date needs the exact floor of a quotient, and a comparison of bounds that are as large as a number can be, so it works on {numerator, scale}, which is numerator / 10^scale, with Elixir's integers, which have no limit.

iex> x = Feel.Type.ExactNumber.fraction(Decimal.new("-2.5"))
iex> {x, Feel.Type.ExactNumber.floor_of(x), Feel.Type.ExactNumber.ceiling_of(x)}
{{-25, 1}, -3, -2}
iex> Feel.Type.ExactNumber.floor_div(Feel.Type.ExactNumber.fraction(Decimal.new("86399.5")), 86_400)
0

Summary

Types

t()

{numerator, scale}: the number numerator / 10^scale.

Functions

The sum of two exact numbers.

The ceiling of the number over a whole number above zero.

The least whole number not below the number.

The Decimal of an exact number, with no zero at the end of its fraction, so that the same number is the same term (0.50 is 0.5 and 1.0 is 1). A whole number of more than 34 digits is written with an exponent, as a number of FEEL is (fit/2 gives {5 * 10^6000, 0} for a bound of that size, and Decimal.new(1, 5 * 10^6000, 0) is not a number the run takes, D-59): its zeros are the exponent, so that it is 5E+6000, as the literal is.

The number rounded to 34 significant digits, as a number of FEEL is (the run rounds the seconds between two times, and the value of a date and time, to that), in the direction given: :down for a lower bound, :up for an upper one, so that what the run rounds to (half to even, to the nearest) is between them.

The floor of the number over a whole number above zero.

The greatest whole number not above the number.

The exact number that a Decimal is.

The greater of two exact numbers.

The lesser of two exact numbers.

The difference of two exact numbers.

The number of 0 at the end of the digits of a whole number that is not zero (its first digit is not 0), found by halving: a run of 6,000 zeros, as the bound max of the numbers of FEEL has when a preimage is written back as a Decimal, is about 13 comparisons of a binary, and String.trim_trailing/2 took 6,000 steps for it, twice for each fact of a rounding function. The last m digits are all 0 for every m up to the number of zeros and for none above it, and that is what is searched for.

Two exact numbers over the same power of ten: {first, second, scale}.

The first and the last whole number of an interval low..high, where each end is in (:closed) or not (:open), or nil if it has none.

Types

t()

@type t() :: {integer(), non_neg_integer()}

{numerator, scale}: the number numerator / 10^scale.

Functions

add(a, b)

@spec add(t(), t()) :: t()

The sum of two exact numbers.

ceiling_div(arg, divisor)

@spec ceiling_div(t(), pos_integer()) :: integer()

The ceiling of the number over a whole number above zero.

ceiling_of(arg)

@spec ceiling_of(t()) :: integer()

The least whole number not below the number.

compare(a, b)

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

decimal(numerator, scale)

@spec decimal(integer(), non_neg_integer()) :: Decimal.t()

The Decimal of an exact number, with no zero at the end of its fraction, so that the same number is the same term (0.50 is 0.5 and 1.0 is 1). A whole number of more than 34 digits is written with an exponent, as a number of FEEL is (fit/2 gives {5 * 10^6000, 0} for a bound of that size, and Decimal.new(1, 5 * 10^6000, 0) is not a number the run takes, D-59): its zeros are the exponent, so that it is 5E+6000, as the literal is.

iex> Feel.Type.ExactNumber.decimal(50, 2)
Decimal.new("0.5")
iex> Feel.Type.ExactNumber.decimal(10, 1)
Decimal.new(1)
iex> Feel.Type.ExactNumber.decimal(0, 5)
Decimal.new(0)
iex> Feel.Type.ExactNumber.decimal(100, 0)
Decimal.new(100)
iex> Feel.Type.ExactNumber.decimal(-5 * Integer.pow(10, 40), 0)
Decimal.new("-5E+40")

fit(arg, direction)

@spec fit(t(), :down | :up) :: t()

The number rounded to 34 significant digits, as a number of FEEL is (the run rounds the seconds between two times, and the value of a date and time, to that), in the direction given: :down for a lower bound, :up for an upper one, so that what the run rounds to (half to even, to the nearest) is between them.

iex> alias Feel.Type.ExactNumber, as: E
iex> E.fit({12_345_678_901_234_567_890_123_456_789_012_345_678, 4}, :down)
{1_234_567_890_123_456_789_012_345_678_901_234, 0}
iex> E.fit({12_345_678_901_234_567_890_123_456_789_012_345_678, 4}, :up)
{1_234_567_890_123_456_789_012_345_678_901_235, 0}
iex> E.fit({5, 1}, :up)
{5, 1}

floor_div(arg, divisor)

@spec floor_div(t(), pos_integer()) :: integer()

The floor of the number over a whole number above zero.

floor_of(arg)

@spec floor_of(t()) :: integer()

The greatest whole number not above the number.

fraction(decimal)

@spec fraction(Decimal.t()) :: t()

The exact number that a Decimal is.

greater(a, b)

@spec greater(t(), t()) :: t()

The greater of two exact numbers.

lesser(a, b)

@spec lesser(t(), t()) :: t()

The lesser of two exact numbers.

negate(arg)

@spec negate(t()) :: t()

subtract(a, b)

@spec subtract(t(), t()) :: t()

The difference of two exact numbers.

trailing_zeros(digits)

@spec trailing_zeros(String.t()) :: non_neg_integer()

The number of 0 at the end of the digits of a whole number that is not zero (its first digit is not 0), found by halving: a run of 6,000 zeros, as the bound max of the numbers of FEEL has when a preimage is written back as a Decimal, is about 13 comparisons of a binary, and String.trim_trailing/2 took 6,000 steps for it, twice for each fact of a rounding function. The last m digits are all 0 for every m up to the number of zeros and for none above it, and that is what is searched for.

iex> Feel.Type.ExactNumber.trailing_zeros("12300")
2
iex> Feel.Type.ExactNumber.trailing_zeros("7")
0
iex> Feel.Type.ExactNumber.trailing_zeros("1" <> String.duplicate("0", 6000))
6000

unify(arg1, arg2)

@spec unify(t(), t()) :: {integer(), integer(), non_neg_integer()}

Two exact numbers over the same power of ten: {first, second, scale}.

whole(arg1, arg2)

@spec whole({t(), :open | :closed}, {t(), :open | :closed}) ::
  {integer(), integer()} | nil

The first and the last whole number of an interval low..high, where each end is in (:closed) or not (:open), or nil if it has none.

iex> alias Feel.Type.ExactNumber, as: E
iex> E.whole({E.fraction(Decimal.new("0.5")), :closed}, {E.fraction(Decimal.new(3)), :open})
{1, 2}
iex> E.whole({E.fraction(Decimal.new(1)), :open}, {E.fraction(Decimal.new(2)), :open})
nil