# `Localize.Utils.Math`
[🔗](https://github.com/elixir-localize/localize/blob/v0.22.0/lib/localize/utils/math.ex#L1)

Mathematical helper functions for number formatting and
Localize subsystems.

Provides arithmetic operations (add, subtract, multiply, divide),
modulo, rounding (to arbitrary precision with selectable rounding
modes), logarithms, powers, roots, square roots, significant-digit
rounding, coefficient/exponent decomposition, and float-to-rational
conversion.

All arithmetic operations accept integers, floats, and Decimals and
return the most appropriate type.

# `normalised_decimal`

```elixir
@type normalised_decimal() ::
  {%Decimal{coef: term(), exp: term(), sign: term()}, integer()}
```

# `number_or_decimal`

```elixir
@type number_or_decimal() :: number() | Decimal.t()
```

# `rounding`

```elixir
@type rounding() ::
  :down | :half_up | :half_even | :ceiling | :floor | :half_down | :up
```

# `add`

```elixir
@spec add(number_or_decimal(), number_or_decimal()) :: number_or_decimal()
```

Adds two numbers together.

The numbers can be integers, floats, or Decimals.
If either argument is a Decimal the result will be a Decimal.
If both arguments are integers the result is an integer;
if either is a float the result is a float.

### Arguments

* `number_1` is an integer, float, or Decimal.

* `number_2` is an integer, float, or Decimal.

### Returns

* The sum of the two numbers.

### Examples

    iex> Localize.Utils.Math.add(1, 2)
    3

    iex> Localize.Utils.Math.add(Decimal.new("1.5"), 2)
    Decimal.new("3.5")

# `amod`

```elixir
@spec amod(number_or_decimal(), number_or_decimal()) :: number_or_decimal()
```

Returns the adjusted modulus of `x` and `y`.

If the modulo result is zero, returns `y` instead.

### Arguments

* `x` is an integer, float, or Decimal.

* `y` is an integer, float, or Decimal.

### Returns

* The adjusted modulo result.

### Examples

    iex> Localize.Utils.Math.amod(10, 5)
    5

    iex> Localize.Utils.Math.amod(7, 3)
    1

# `approximation_error`

Calculates the error between a float and a rational approximation.

### Arguments

* `original` is the original float.

* `{numerator, denominator}` is the approximate ratio.

### Returns

* The absolute error as a float.

### Examples

    iex> Localize.Utils.Math.approximation_error(0.75, {3, 4})
    0.0

# `coef_exponent`

```elixir
@spec coef_exponent(number_or_decimal()) :: {number_or_decimal(), integer()}
```

Returns a tuple representing a number in normalized form with
the mantissa in the range `0 < m < 10` and a base 10 exponent.

### Arguments

* `number` is an integer, float, or Decimal.

### Returns

* A tuple `{mantissa, exponent}` where the mantissa is in the
  range `0 < m < 10`.

### Examples

    iex> Localize.Utils.Math.coef_exponent(Decimal.new(465))
    {Decimal.new("4.65"), 2}

# `coef_exponent_digits`

```elixir
@spec coef_exponent_digits(number_or_decimal()) ::
  {Localize.Utils.Digits.t(), integer()}
```

Returns a tuple representing a number in normalized form with
the mantissa as a `Digits.t` tuple and a base 10 exponent.

### Arguments

* `number` is an integer, float, or Decimal.

### Returns

* A tuple `{digits_tuple, exponent}` where `digits_tuple` is of
  the form `{digit_list, place, sign}`.

# `convergents`

Calculates convergents (rational approximations) from continued
fraction coefficients.

### Arguments

* `coefficients` is a list of continued fraction coefficients.

### Returns

* A list of `{numerator, denominator}` tuples.

### Examples

    iex> Localize.Utils.Math.convergents([3, 7, 15, 1])
    [{333, 106}, {355, 113}, {22, 7}, {333, 106}, {3, 1}, {22, 7}]

# `default_rounding`

```elixir
@spec default_rounding() :: 3
```

Returns the default number of rounding digits.

### Returns

* An integer (`3`).

### Examples

    iex> Localize.Utils.Math.default_rounding()
    3

# `default_rounding_mode`

```elixir
@spec default_rounding_mode() :: :half_even
```

Returns the default rounding mode for rounding operations.

### Returns

* The atom `:half_even`.

### Examples

    iex> Localize.Utils.Math.default_rounding_mode()
    :half_even

# `div`

```elixir
@spec div(number_or_decimal(), number_or_decimal()) :: Decimal.t()
```

Divides one number by another.

The numbers can be integers, floats, or Decimals.
If either argument is a Decimal the result will be a Decimal.
If both arguments are plain numbers the result will be a Decimal.

### Arguments

* `number_1` is an integer, float, or Decimal.

* `number_2` is an integer, float, or Decimal.

### Returns

* The quotient of the two numbers as a Decimal.

### Examples

    iex> Localize.Utils.Math.div(Decimal.new(10), 2)
    Decimal.new("5")

# `div_amod`

```elixir
@spec div_amod(integer(), integer()) :: {integer(), integer()}
```

Returns the adjusted quotient and remainder of two integers.

This version returns the divisor if the remainder would
otherwise be zero, and decrements the quotient by one.

### Arguments

* `integer_1` is an integer.

* `integer_2` is an integer.

### Returns

* A tuple `{quotient, adjusted_remainder}`.

### Examples

    iex> Localize.Utils.Math.div_amod(10, 5)
    {1, 5}

    iex> Localize.Utils.Math.div_amod(7, 3)
    {2, 1}

# `div_mod`

```elixir
@spec div_mod(integer(), integer()) :: {integer(), integer()}
```

Returns the quotient and remainder of two integers.

### Arguments

* `integer_1` is an integer.

* `integer_2` is an integer.

### Returns

* A tuple `{quotient, remainder}`.

### Examples

    iex> Localize.Utils.Math.div_mod(7, 3)
    {2, 1}

# `float_to_ratio`

Converts a float to a rational number `{numerator, denominator}`.

### Arguments

* `x` is any float.

* `options` is a keyword list of options.

### Options

* `:max_iterations` - Maximum number of continued fraction terms
  (default: 20).

* `:epsilon` - Tolerance for float comparisons (default: 1.0e-10).

* `:max_denominator` - Maximum allowed denominator (default: nil,
  meaning no limit).

### Returns

* A tuple `{numerator, denominator}`.

### Examples

    iex> Localize.Utils.Math.float_to_ratio(0.75)
    {3, 4}

    iex> Localize.Utils.Math.float_to_ratio(3.14159, max_iterations: 5)
    {9208, 2931}

    iex> Localize.Utils.Math.float_to_ratio(3.14159, max_denominator: 10)
    {22, 7}

# `log10`

```elixir
@spec log10(number_or_decimal()) :: float() | Decimal.t()
```

Returns the log base 10 of a number.

For integers and floats it calls the BIF `:math.log10/1` function.
For Decimals the identity `log10(x) = ln(x) / ln(10)` is used.

### Arguments

* `number` is an integer, float, or Decimal.

### Returns

* The base-10 logarithm of the number.

### Examples

    iex> Localize.Utils.Math.log10(100)
    2.0

    iex> Localize.Utils.Math.log10(123)
    2.089905111439398

    iex> Localize.Utils.Math.log10(Decimal.new(9000))
    Decimal.new("3.953767554157656512064441441")

# `log`

Returns the natural log of a number.

For integers and floats it calls the BIF `:math.log/1` function.
For Decimals the log is computed using a series expansion.

### Arguments

* `number` is an integer, float, or Decimal.

### Returns

* The natural logarithm of the number.

### Examples

    iex> Localize.Utils.Math.log(123)
    4.812184355372417

    iex> Localize.Utils.Math.log(Decimal.new(9000))
    Decimal.new("9.103886231350952380952380952")

# `maybe_integer`

Converts a Decimal to an integer if possible, otherwise returns
the value unchanged.

### Arguments

* `number` is a Decimal, float, or integer.

### Returns

* An integer if the number has no fractional part.

* The original value otherwise.

### Examples

    iex> Localize.Utils.Math.maybe_integer(Decimal.new("3.0"))
    3

    iex> Localize.Utils.Math.maybe_integer(2.0)
    2

    iex> Localize.Utils.Math.maybe_integer(5)
    5

# `mod`

```elixir
@spec mod(number_or_decimal(), number_or_decimal()) :: number_or_decimal()
```

Calculates the modulo of a number (integer, float, or Decimal).

Uses floored division rather than truncated division. This
matches the semantics required by the CLDR plural rules
specification.

### Arguments

* `number` is an integer, float, or Decimal.

* `modulus` is an integer, float, or Decimal.

### Returns

* The modulo result in the same type as the input.

### Examples

    iex> Localize.Utils.Math.mod(1234.0, 5)
    4.0

    iex> Localize.Utils.Math.mod(7, 3)
    1

# `mult`

```elixir
@spec mult(number_or_decimal(), number_or_decimal()) :: number_or_decimal()
```

Multiplies two numbers together.

The numbers can be integers, floats, or Decimals.
If either argument is a Decimal the result will be a Decimal.
If both arguments are integers the result is an integer;
if either is a float the result is a float.

### Arguments

* `number_1` is an integer, float, or Decimal.

* `number_2` is an integer, float, or Decimal.

### Returns

* The product of the two numbers.

### Examples

    iex> Localize.Utils.Math.mult(3, 4)
    12

    iex> Localize.Utils.Math.mult(Decimal.new("1.5"), 2)
    Decimal.new("3.0")

# `pow`

Raises one number to an exponent.

Delegates to `power/2`.

### Arguments

* `n` is the base number.

* `m` is the exponent.

### Returns

* The result of `n` raised to the power `m`.

# `power`

```elixir
@spec power(number_or_decimal(), number_or_decimal()) :: number_or_decimal()
```

Raises a number to an integer power.

Uses the binary exponentiation method. For Decimal numbers
raising 10 to a power, the exponent is shifted directly for
efficiency.

### Arguments

* `number` is an integer, float, or Decimal.

* `n` is an integer exponent (or Decimal integer).

### Returns

* The number raised to the given power.

### Examples

    iex> Localize.Utils.Math.power(10, 2)
    100

    iex> Localize.Utils.Math.power(10, 3)
    1000

    iex> Localize.Utils.Math.power(2, 10)
    1024

# `power_of_10`

```elixir
@spec power_of_10(integer()) :: number()
```

Returns 10 raised to the given power.

Powers 0 through 326 are precomputed for efficiency.
Negative powers return the reciprocal.

### Arguments

* `n` is an integer exponent.

### Returns

* An integer for non-negative exponents.

* A float for negative exponents.

### Examples

    iex> Localize.Utils.Math.power_of_10(0)
    1

    iex> Localize.Utils.Math.power_of_10(3)
    1000

    iex> Localize.Utils.Math.power_of_10(-1)
    0.1

# `root`

Calculates the nth root of a number.

### Arguments

* `number` is an integer or a Decimal.

* `nth` is a positive integer.

### Returns

* The nth root of the number.

### Examples

    iex> Localize.Utils.Math.root(Decimal.new(8), 3)
    Decimal.new("2.0")

    iex> Localize.Utils.Math.root(Decimal.new(27), 3)
    Decimal.new("3.0")

# `round`

Rounds a number to an arbitrary precision using one of several
rounding algorithms.

Rounding algorithms are based on the definitions given in IEEE 754,
but also include two additional options.

### Arguments

* `number` is a float, integer, or Decimal.

* `places` is an integer number of decimal places to round to.
  Default is `0`.

* `mode` is the rounding mode to be applied. Default is `:half_even`.

### Rounding modes

Directed roundings:

* `:down` - Round towards 0 (truncate).

* `:up` - Round away from 0.

* `:ceiling` - Round toward positive infinity.

* `:floor` - Round toward negative infinity.

Round to nearest:

* `:half_even` - Round to nearest value; tiebreak rounds towards
  the nearest even value. This is the default for IEEE binary
  floating-point.

* `:half_up` - Round to nearest value; tiebreak rounds away from 0.

* `:half_down` - Round to nearest value; tiebreak rounds towards 0.

### Returns

* The rounded number.

### Examples

    iex> Localize.Utils.Math.round(1.5, 0, :half_even)
    2.0

# `round_significant`

```elixir
@spec round_significant(number_or_decimal(), integer()) :: number_or_decimal()
```

Rounds a number to a specified number of significant digits.

This is not the same as rounding fractional digits which is performed
by `Decimal.round/2` and `Float.round/2`.

### Arguments

* `number` is a float, integer, or Decimal.

* `n` is the number of significant digits to which the `number`
  should be rounded.

### Returns

* The number rounded to `n` significant digits.

### Examples

    iex> Localize.Utils.Math.round_significant(3.14159, 3)
    3.14

    iex> Localize.Utils.Math.round_significant(10.3554, 1)
    10.0

    iex> Localize.Utils.Math.round_significant(0.00035, 1)
    0.0004

# `rounding_modes`

Returns the list of valid rounding modes.

### Returns

* A list of rounding mode atoms.

# `sqrt`

Calculates the square root of a Decimal number using Newton's method.

For integers and floats, delegates to the erlang `:math` module.
For Decimals, an initial estimate from `:math.sqrt` is refined
iteratively.

### Arguments

* `number` is an integer, float, or Decimal.

* `precision` is the desired precision (default: 0.0001).

### Returns

* The square root of the number.

### Examples

    iex> Localize.Utils.Math.sqrt(Decimal.new(9))
    Decimal.new("3.0")

    iex> Localize.Utils.Math.sqrt(Decimal.new("9.869"))
    Decimal.new("3.141496458696078173887197038")

# `sub`

```elixir
@spec sub(number_or_decimal(), number_or_decimal()) :: number_or_decimal()
```

Subtracts one number from another.

The numbers can be integers, floats, or Decimals.
If either argument is a Decimal the result will be a Decimal.
If both arguments are integers the result is an integer;
if either is a float the result is a float.

### Arguments

* `number_1` is an integer, float, or Decimal.

* `number_2` is an integer, float, or Decimal.

### Returns

* The difference of the two numbers.

### Examples

    iex> Localize.Utils.Math.sub(5, 3)
    2

    iex> Localize.Utils.Math.sub(Decimal.new("5.5"), 3)
    Decimal.new("2.5")

# `to_float`

```elixir
@spec to_float(Decimal.t()) :: float()
```

Convert a Decimal to a float.

Note that this conversion may lose precision for numbers
that cannot be exactly represented as IEEE 754 floats.

### Arguments

* `decimal` is a `%Decimal{}` struct.

### Returns

* A float value.

### Examples

    iex> Localize.Utils.Math.to_float(Decimal.new("1.5"))
    1.5

# `within`

```elixir
@spec within(number(), Range.t()) :: boolean()
```

Check if a `number` is within a `range`.

For integers the comparison uses the standard `in` operator.
For floats the comparison checks that the float has no
fractional part and falls within the range endpoints.

### Arguments

* `number` is an integer or float.

* `range` is an Elixir `Range`.

### Returns

* `true` if the number is within the range.

* `false` otherwise.

### Examples

    iex> Localize.Utils.Math.within(2, 1..3)
    true

    iex> Localize.Utils.Math.within(2.0, 1..3)
    true

    iex> Localize.Utils.Math.within(2.1, 1..3)
    false

---

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