Search documentation of Localize

# Localize.Utils.Math (Localize v0.39.0)

Copy Markdown View Source

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.

## Summary

### Types

normalised_decimal()
number_or_decimal()
rounding()

### Functions

add(number_1, number_2)

Adds two numbers together.

amod(x, y)

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

approximation_error(original, arg)

Calculates the error between a float and a rational approximation.

coef_exponent(number)

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

coef_exponent_digits(number)

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

convergents(list)

Calculates convergents (rational approximations) from continued fraction coefficients.

default_rounding()

Returns the default number of rounding digits.

default_rounding_mode()

Returns the default rounding mode for rounding operations.

div(number_1, number_2)

Divides one number by another.

div_amod(integer_1, integer_2)

Returns the adjusted quotient and remainder of two integers.

div_mod(integer_1, integer_2)

Returns the quotient and remainder of two integers.

float_to_ratio(x, options \\ [])

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

log10(number)

Returns the log base 10 of a number.

log(number)

Returns the natural log of a number.

maybe_integer(number)

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

mod(number, modulus)

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

mult(number_1, number_2)

Multiplies two numbers together.

pow(n, m)

Raises one number to an exponent.

power(number, n)

Raises a number to an integer power.

power_of_10(n)

Returns 10 raised to the given power.

root(number, nth)

Calculates the nth root of a number.

round(number, places \\ 0, mode \\ :half_even)

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

round_significant(number, n)

Rounds a number to a specified number of significant digits.

rounding_modes()

Returns the list of valid rounding modes.

sqrt(number, precision \\ nil)

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

sub(number_1, number_2)

Subtracts one number from another.

to_float(decimal)

Convert a Decimal to a float.

within(number, range)

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

## Types

# normalised_decimal()

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

# number_or_decimal()

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

# rounding()

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

## Functions

# add(number_1, number_2)

    @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(x, y)

    @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(original, arg)

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(number)

    @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(number)

    @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(list)

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()

    @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()

    @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(number_1, number_2)

    @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(integer_1, integer_2)

    @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(integer_1, integer_2)

    @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(x, options \\ [])

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(number)

    @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.953767554157656512064441441036289")

# log(number)

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.103886231350952380952380952380952")

# maybe_integer(number)

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(number, modulus)

    @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(number_1, number_2)

    @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(n, m)

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(number, n)

    @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(n)

    @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(number, nth)

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(number, places \\ 0, mode \\ :half_even)

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(number, n)

    @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(number, precision \\ nil)

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

For `Decimal` input the result is rounded to the precision of the current process' `Decimal.Context` (see `Decimal.Context.get/0`). The Decimal 2.x default is 28 digits; the IEEE 754 decimal128 default in Decimal 3.0 is 34. Set a different precision with `Decimal.Context.set/1` or `Decimal.Context.with/2` to change the result's precision.

Integer and float inputs are delegated to `:math.sqrt/1`.

### Arguments

- `number` is the number whose square root is to be calculated. May be an integer, a float, or a `Decimal.t/0`.

- `precision` is the absolute convergence threshold used by Newton's method when `number` is a `Decimal.t/0`. When `nil` (the default), the threshold is `10^-precision` where `precision` is the current `Decimal.Context` precision. May also be passed as an integer, a float, or a `Decimal.t/0`. Ignored for integer and float input.

### Returns

- A `Decimal.t/0` rounded to the current `Decimal.Context` precision when `number` is a `Decimal.t/0`.

- A float when `number` is an integer or float.

### Examples

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

    iex> Decimal.Context.with(%{Decimal.Context.get() | precision: 28}, fn ->
    ...>   Localize.Utils.Math.sqrt(Decimal.new("9.869"))
    ...> end)
    Decimal.new("3.141496458696078173887197038")

# sub(number_1, number_2)

    @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(decimal)

    @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(number, range)

    @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