Localize.Utils.Math (Localize v0.22.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

Functions

Adds two numbers together.

Returns the adjusted modulus of x and y.

Calculates the error between a float and a rational approximation.

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

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

Calculates convergents (rational approximations) from continued fraction coefficients.

Returns the default number of rounding digits.

Returns the default rounding mode for rounding operations.

Divides one number by another.

Returns the adjusted quotient and remainder of two integers.

Returns the quotient and remainder of two integers.

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

Returns the log base 10 of a number.

Returns the natural log of a number.

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

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

Multiplies two numbers together.

Raises one number to an exponent.

Raises a number to an integer power.

Returns 10 raised to the given power.

Calculates the nth root of a number.

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

Rounds a number to a specified number of significant digits.

Returns the list of valid rounding modes.

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

Subtracts one number from another.

Convert a Decimal to a float.

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)

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)

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)

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

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

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)

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)

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)

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 \\ 0.0001)

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

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