Packages
ex_cldr
0.0.10
2.47.5
2.47.4
2.47.3
2.47.2
2.47.1
2.47.0
2.46.0
2.45.2
2.45.1
2.45.0
2.44.1
2.44.0
2.43.2
2.43.1
2.43.0
2.42.0
2.41.1
2.41.0
2.40.4
2.40.3
2.40.2
2.40.1
2.40.0
2.39.2
2.39.1
2.39.0
2.38.1
2.38.0
2.37.5
2.37.4
2.37.3
retired
2.37.2
2.37.1
2.37.0
2.36.0
2.35.1
2.35.0
retired
2.34.2
2.34.1
2.34.0
2.33.2
2.33.1
2.33.1-rc.0
2.33.0
2.32.1
2.32.0
2.31.0
2.30.0
2.29.0
2.28.0
2.27.1
2.27.0
retired
2.26.3
2.26.2
2.26.1
2.26.0
2.25.0
2.24.2
2.24.1
2.24.0
2.24.0-rc.6
2.24.0-rc.5
2.24.0-rc.4
2.24.0-rc.3
2.24.0-rc.2
2.24.0-rc.1
2.24.0-rc.0
2.23.2
2.23.1
2.23.0
2.22.1
2.22.0
2.21.0
2.20.0
2.20.0-rc.3
2.20.0-rc.2
2.20.0-rc.1
2.20.0-rc.0
2.19.1
2.19.0
2.18.2
2.18.1
2.18.0
2.18.0-rc.0
2.17.2
2.17.1
2.17.0
2.16.2
2.16.1
2.16.1-rc.0
2.16.0
2.15.0
2.14.1
2.14.0
2.14.0-rc.0
2.13.0
2.12.1
2.12.0
2.11.1
2.11.0
2.10.2
2.10.1
2.10.0
retired
2.9.0
2.8.1
2.8.0
2.7.2
2.7.1
2.7.0
2.6.2
2.6.1
2.6.0
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.2
2.3.1
2.3.0
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.0
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.3
retired
2.0.0-rc.2
retired
2.0.0-rc.1
retired
2.0.0-rc.0
retired
1.8.2
1.8.1
1.8.0
1.7.1
1.7.0
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.2
1.5.1
1.5.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
retired
1.4.0-rc.3
retired
1.4.0-rc.2
retired
1.4.0-rc.1
retired
1.4.0-rc.0
retired
1.3.2
1.3.1
1.3.0
1.2.0
1.1.0
1.0.0
1.0.0-rc.3
retired
1.0.0-rc.2
retired
1.0.0-rc.1
retired
1.0.0-rc.0
retired
0.13.1
0.13.0
0.12.2
retired
0.12.1
retired
0.12.0
retired
0.11.0
retired
0.10.0
retired
0.9.0
retired
0.8.3
retired
0.8.2
retired
0.8.1
retired
0.8.0
retired
0.7.0
retired
0.6.2
retired
0.6.1
retired
0.6.0
retired
0.5.2
retired
0.5.1
retired
0.5.0
retired
0.4.2
retired
0.4.1
retired
0.4.0
retired
0.3.0
retired
0.2.1
retired
0.2.0
retired
0.1.3
retired
0.1.2
retired
0.1.1
retired
0.1.0
retired
0.0.20
retired
0.0.19
retired
0.0.18
retired
0.0.17
retired
0.0.16
retired
0.0.15
retired
0.0.14
retired
0.0.13
retired
0.0.12
retired
0.0.11
retired
0.0.10
retired
0.0.8
retired
0.0.7
retired
0.0.6
retired
0.0.5
retired
0.0.4
retired
0.0.3
retired
Common Locale Data Repository (CLDR) functions for Elixir to localize and format numbers, dates, lists, messages, languages, territories and units with support for over 700 locales for internationalized (i18n) and localized (L10N) applications.
Retired package: Deprecated
Current section
Files
Jump to
Current section
Files
lib/cldr/number/math.ex
defmodule Cldr.Number.Math do
@moduledoc """
Math helper functions for number formatting
"""
@type number_or_decimal :: number | %Decimal{}
@type normalised_decimal :: {%Decimal{}, integer}
@default_rounding 3
@zero Decimal.new(0)
@one Decimal.new(1)
@two Decimal.new(2)
@minus_one Decimal.new(-1)
@ten Decimal.new(10)
@doc """
Returns the default rounding used by Cldr.
This function is used to control `fraction_as_integer/2`
and any other Cldr function that takes a `rounding` argument.
"""
@spec default_rounding :: integer
def default_rounding do
@default_rounding
end
@doc """
Returns the fractional part of a float or Decimal as an integer.
* `number` can be either a float, Decimal or integer although an integer has
no fraction part and will therefore always return 0.
* `rounding` is the precision applied on each internal iteration as the
fraction is converted to an integer. The default rounding is
`default_rounding()`.
## Examples
iex> Cldr.Number.Math.fraction_as_integer(123.456)
456
iex> Cldr.Number.Math.fraction_as_integer(123.456, 2)
46
iex> Cldr.Number.Math.fraction_as_integer(Decimal.new("123.456"), 3)
456
iex> Cldr.Number.Math.fraction_as_integer(1999, 3)
0
"""
@spec fraction_as_integer(number_or_decimal, integer) :: integer
def fraction_as_integer(fraction, rounding \\ @default_rounding)
def fraction_as_integer(fraction, rounding)
when is_float(fraction) and fraction > 1.0 do
fraction_as_integer(fraction - trunc(fraction), rounding)
end
def fraction_as_integer(fraction, rounding) when is_float(fraction) do
do_fraction_as_integer(fraction, rounding)
end
def fraction_as_integer(%Decimal{} = fraction, rounding) do
if Decimal.cmp(fraction, @one) == :gt do
fraction
|> Decimal.sub(Decimal.round(fraction, 0, :floor))
|> fraction_as_integer(rounding)
else
do_fraction_as_integer(fraction, rounding)
end
end
def fraction_as_integer(fraction, _rounding)
when is_integer(fraction) do
0
end
defp do_fraction_as_integer(fraction, rounding) when is_float(fraction) do
truncated_fraction = trunc(fraction)
if truncated_fraction == fraction do
truncated_fraction
else
fraction
|> Float.round(rounding)
|> Kernel.*(10)
|> do_fraction_as_integer(rounding)
end
end
defp do_fraction_as_integer(fraction, rounding) when is_map(fraction) do
truncated_fraction = Decimal.round(fraction, 0, :floor)
if Decimal.equal?(truncated_fraction, fraction) do
truncated_fraction |> Decimal.to_integer
else
fraction
|> Decimal.round(rounding)
|> Decimal.mult(@ten)
|> do_fraction_as_integer(rounding)
end
end
@doc """
Returns the number of decimal digits in the integer
part of a number.
* `number` can be an integer, float or `Decimal`.
## Examples
iex(10)> Cldr.Number.Math.number_of_integer_digits(1234)
4
iex(11)> Cldr.Number.Math.number_of_integer_digits(Decimal.new("123456789"))
9
iex(15)> Cldr.Number.Math.number_of_integer_digits(1234.456)
4
"""
@spec number_of_integer_digits(number_or_decimal) :: integer
def number_of_integer_digits(%Decimal{exp: exp} = number) when exp < 0 do
number
|> Decimal.round(0, :floor)
|> Decimal.to_integer
|> number_of_integer_digits
end
# +/- 0,xxxxx
def number_of_integer_digits(number)
when is_number(number) and number < 1 and number > -1 do
0
end
def number_of_integer_digits(%Decimal{} = number) do
number
|> Decimal.to_integer
|> number_of_integer_digits
end
def number_of_integer_digits(number) when is_float(number) do
number
|> trunc
|> number_of_integer_digits
end
def number_of_integer_digits(number) when is_integer(number) do
Integer.digits(number)
|> Enum.count
end
@doc """
Remove trailing zeroes from an integer.
* `number` must be an integer.
## Examples
iex> Cldr.Number.Math.remove_trailing_zeros(1234000)
1234
"""
@spec remove_trailing_zeros(integer) :: integer
def remove_trailing_zeros(number)
when is_integer(number) and number == 0 do
number
end
def remove_trailing_zeros(number)
when is_integer(number) do
if rem(number, 10) != 0 do
number
else
number
|> div(10)
|> remove_trailing_zeros()
end
end
@doc """
Check if a `number` is within a `range`.
* `number` is either an integer or a float.
When an integer, the comparison is made using the standard Elixir `in`
operator.
When `number` is a float the comparison is made using the `>=` and `<=`
operators on the range endpoints. Note the comparison for a float is only for
floats that have no fractional part. If a float has a fractional part then
`within` returns `false`.
*Since this function is only provided to support plural rules, the float
comparison is only useful if the float has no fractional part.*
## Examples
iex> Cldr.Number.Math.within(2.0, 1..3)
true
iex> Cldr.Number.Math.within(2.1, 1..3)
false
"""
@spec within(number, integer) :: boolean
def within(number, range) when is_integer(number) do
number in range
end
# When checking if a decimal is in a range it is only
# valid if there are no decimal places
def within(number, first..last) when is_float(number) do
number == trunc(number) && number >= first && number <= last
end
@doc """
Calculates the modulo of a number (integer, float or Decimal).
Note that this function uses `floored division` whereas the builtin `rem`
function uses `truncated division`. See `Decimal.rem/2` if you want a
`truncated division` function for Decimals that will return the same value as
the BIF `rem/2` but in Decimal form.
See [Wikipedia](https://en.wikipedia.org/wiki/Modulo_operation) for an
explanation of the difference.
## Examples
iex> Cldr.Number.Math.mod(1234.0, 5)
4.0
iex> Cldr.Number.Math.mod(Decimal.new("1234.456"), 5)
#Decimal<4.456>
iex> Cldr.Number.Math.mod(Decimal.new(123.456), Decimal.new(3.4))
#Decimal<1.056>
iex> Cldr.Number.Math.mod Decimal.new(123.456), 3.4
#Decimal<1.056>
"""
@spec mod(number_or_decimal, number_or_decimal) ::
float | %Decimal{}
def mod(number, modulus) when is_float(number) do
number - (Float.floor(number / modulus) * modulus)
end
def mod(number, modulus) when is_integer(number) do
modulo = number
|> Kernel./(modulus)
|> Float.floor
|> Kernel.*(modulus)
number - modulo
end
def mod(%Decimal{} = number, %Decimal{} = modulus) do
modulo = number
|> Decimal.div(modulus)
|> Decimal.round(0, :floor)
|> Decimal.mult(modulus)
Decimal.sub(number, modulo)
end
def mod(%Decimal{} = number, modulus) when is_number(modulus) do
mod(number, Decimal.new(modulus))
end
@doc """
Convert a Decimal to a float
* `decimal` must be a Decimal
This is very likely to lose precision - lots of numbers won't
make the round trip conversion. Use with care. Actually, better
not to use it at all.
"""
@spec to_float(%Decimal{}) :: float
def to_float(%Decimal{sign: sign, coef: coef, exp: exp}) do
sign * coef * 1.0 * :math.pow(10, exp)
end
@doc """
Rounds a number to a specified number of significant digits.
This is not the same as rounding decimals which is performed
by `Decimal.round/2`.
* `number` is a float, integer or Decimal
* `n` is the number of significant digits to which the `number` should be
rounded
## Examples
iex> Cldr.Number.Math.round_significant(3.14159, 3)
3.14
iex> Cldr.Number.Math.round_significant(10.3554, 1)
10.0
iex> Cldr.Number.Math.round_significant(0.00035, 1)
0.0004
## More on significant digits
* 3.14159 has six significant digits (all the numbers give you useful
information)
* 1000 has one significant digit (only the 1 is interesting; you don't know
anything for sure about the hundreds, tens, or units places; the zeroes may
just be placeholders; they may have rounded something off to get this value)
* 1000.0 has five significant digits (the ".0" tells us something interesting
about the presumed accuracy of the measurement being made: that the
measurement is accurate to the tenths place, but that there happen to be
zero tenths)
* 0.00035 has two significant digits (only the 3 and 5 tell us something; the
other zeroes are placeholders, only providing information about relative
size)
* 0.000350 has three significant digits (that last zero tells us that the
measurement was made accurate to that last digit, which just happened to
have a value of zero)
* 1006 has four significant digits (the 1 and 6 are interesting, and we have
to count the zeroes, because they're between the two interesting numbers)
* 560 has two significant digits (the last zero is just a placeholder)
* 560.0 has four significant digits (the zero in the tenths place means that
the measurement was made accurate to the tenths place, and that there just
happen to be zero tenths; the 5 and 6 give useful information, and the
other zero is between significant digits, and must therefore also be
counted)
Many thanks to [Stackoverflow](http://stackoverflow.com/questions/202302/rounding-to-an-arbitrary-number-of-significant-digits)
"""
@spec round_significant(number_or_decimal, integer) :: number_or_decimal
def round_significant(number, n) when is_number(number) do
sign = if number < 0, do: -1, else: 1
number = abs(number)
d = Float.ceil(:math.log10(number))
power = n - d
magnitude = :math.pow(10, power)
shifted = Float.round(number * magnitude)
rounded = shifted / magnitude
sign * if is_integer(number) do
trunc(rounded)
else
rounded
end
end
def round_significant(%Decimal{sign: sign} = number, n) when sign < 0 do
round_significant(Decimal.abs(number), n) |> Decimal.minus
end
def round_significant(%Decimal{sign: sign} = number, n) when sign > 0 do
d = number |> log10 |> Decimal.round(0, :ceiling)
raised = n |> Decimal.new |> Decimal.sub(d)
magnitude = power(@ten, raised)
shifted = number |> Decimal.mult(magnitude) |> Decimal.round(0)
Decimal.mult(Decimal.div(shifted, magnitude), Decimal.new(sign))
end
@doc """
Return the natural log of a number.
* `number` is an integer, a float or a Decimal
* For integer and float it calls the BIF `:math.log10/1` function.
* For Decimal the log is rolled by hand.
## Examples
iex> Cldr.Number.Math.log(123)
4.812184355372417
iex> Cldr.Number.Math.log(Decimal.new(9000))
#Decimal<9.103886231350952380952380952>
"""
@spec log(number_or_decimal) :: number_or_decimal
def log(number) when is_number(number) do
:math.log(number)
end
@ln10 Decimal.new(2.30258509299)
def log(%Decimal{} = number) do
{mantissa, exp} = mantissa_exponent(number)
exp = Decimal.new(exp)
ln1 = Decimal.mult(exp, @ln10)
sqrt_mantissa = sqrt(mantissa)
y = Decimal.div(Decimal.sub(sqrt_mantissa, @one),
Decimal.add(sqrt_mantissa, @one))
ln2 = y
|> log_polynomial([3,5,7])
|> Decimal.add(y)
|> Decimal.mult(@two)
Decimal.add(Decimal.mult(@two, ln2), ln1)
end
defp log_polynomial(%Decimal{} = value, iterations) do
Enum.reduce iterations, @zero, fn (i, acc) ->
i = Decimal.new(i)
value
|> power(i)
|> Decimal.div(i)
|> Decimal.add(acc)
end
end
@doc """
Return the log10 of a number.
* `number` is an integer, a float or a Decimal
* For integer and float it calls the BIF `:math.log10/1` function.
* For `Decimal`, `log10` is is rolled by hand using the identify `log10(x) =
ln(x) / ln(10)`
## Examples
iex> Cldr.Number.Math.log10(100)
2.0
iex> Cldr.Number.Math.log10(123)
2.089905111439398
iex> Cldr.Number.Math.log10(Decimal.new(9000))
#Decimal<3.953767554157656512064441441>
"""
@spec log10(number_or_decimal) :: number_or_decimal
def log10(number) when is_number(number) do
:math.log10(number)
end
def log10(%Decimal{} = number) do
Decimal.div(log(number), @ln10)
end
@doc """
Raises a number to a integer power.
Raises a number to a power using the the binary method. There is one
exception for Decimal numbers that raise `10` to some power. In this case the
power is calculated by shifting the Decimal exponent which is quite efficient.
For further reading see
[this article](http://videlalvaro.github.io/2014/03/the-power-algorithm.html)
> This function works only with integer exponents!
## Examples
iex> Cldr.Number.Math.power(10, 2)
100
iex> Cldr.Number.Math.power(10, 3)
1000
iex> Cldr.Number.Math.power(10, 4)
10000
iex> Cldr.Number.Math.power(2, 10)
1024
"""
# Decimal number and decimal n
@spec power(number_or_decimal, number_or_decimal) :: number_or_decimal
def power(%Decimal{} = _number, %Decimal{coef: n}) when n == 0 do
@one
end
def power(%Decimal{} = number, %Decimal{coef: n}) when n == 1 do
number
end
def power(%Decimal{} = number, %Decimal{sign: sign} = n) when sign < 1 do
Decimal.div(@one, do_power(number, n, mod(n, @two)))
end
def power(%Decimal{} = number, %Decimal{} = n) do
do_power(number, n, mod(n, @two))
end
# Decimal number and integer/float n
def power(%Decimal{} = _number, n) when n == 0 do
@one
end
def power(%Decimal{} = number, n) when n == 1 do
number
end
# For a decimal we can short cut the multiplications by just
# adjusting the exponent when the coefficient is 10
def power(%Decimal{coef: 10, sign: sign, exp: exp}, n) do
%Decimal{coef: 10, sign: sign, exp: exp + n - 1}
end
def power(%Decimal{} = number, n) when n > 1 do
do_power(number, n, mod(n, 2))
end
def power(%Decimal{} = number, n) when n < 0 do
Decimal.div(@one, do_power(number, abs(n), mod(abs(n), 2)))
end
# For integers and floats
def power(number, n) when n == 0 do
if is_integer(number), do: 1, else: 1.0
end
def power(number, n) when n == 1 do
number
end
def power(number, n) when n > 1 do
do_power(number, n, mod(n, 2))
end
def power(number, n) when n < 1 do
1 / do_power(number, abs(n), mod(abs(n), 2))
end
# Decimal number and decimal n
defp do_power(%Decimal{} = number, %Decimal{coef: coef}, %Decimal{coef: mod})
when mod == 0 and coef == 2 do
Decimal.mult(number, number)
end
defp do_power(%Decimal{} = number, %Decimal{coef: coef} = n, %Decimal{coef: mod})
when mod == 0 and coef != 2 do
power(power(number, Decimal.div(n, @two)), @two)
end
defp do_power(%Decimal{} = number, %Decimal{} = n, _mod) do
Decimal.mult(number, power(number, Decimal.sub(n, @one)))
end
# Decimal number but integer n
defp do_power(%Decimal{} = number, n, mod)
when is_number(n) and mod == 0 and n == 2 do
Decimal.mult(number, number)
end
defp do_power(%Decimal{} = number, n, mod)
when is_number(n) and mod == 0 and n != 2 do
power(power(number, n / 2), 2)
end
defp do_power(%Decimal{} = number, n, _mod)
when is_number(n) do
Decimal.mult(number, power(number, n - 1))
end
# integer/float number and integer/float n
defp do_power(number, n, mod)
when is_number(n) and mod == 0 and n == 2 do
number * number
end
defp do_power(number, n, mod)
when is_number(n) and mod == 0 and n != 2 do
power(power(number, n / 2), 2)
end
defp do_power(number, n, _mod) do
number * power(number, n - 1)
end
@doc """
Returns the number of leading zeros in a
Decimal fraction.
* `number` is any Decimal number
Returns the number of leading zeros in a Decimal number
that is between `-1..1` (ie, has no integer part). If the
number is outside `-1..1` it retuns a negative number, the
`abs` value of which is the number of integer digits in
the number.
## Examples
iex> Cldr.Number.Math.number_of_leading_zeros(Decimal.new(0.0001))
3
iex> Cldr.Number.Math.number_of_leading_zeros(Decimal.new(3.0001))
-1
"""
@spec number_of_leading_zeros(%Decimal{}) :: integer
def number_of_leading_zeros(%Decimal{coef: coef, exp: exp}) do
abs(exp) - number_of_integer_digits(coef)
end
@doc """
Returns a tuple representing a Decimal in a normalized form with
the mantissa in the range `0 < m < 10` and a base 10 exponent.
* `number` must be a Decimal
## Examples
Cldr.Number.Math.mantissa_exponent(Decimal.new(1.23004))
{#Decimal<1.23004>, 0}
Cldr.Number.Math.mantissa_exponent(Decimal.new(465))
{#Decimal<4.65>, 2}
Cldr.Number.Math.mantissa_exponent(Decimal.new(-46.543))
{#Decimal<-4.6543>, 1}
"""
@spec mantissa_exponent(%Decimal{}) :: normalised_decimal
def mantissa_exponent(%Decimal{} = number) do
if between_one_and_minus_one(number) do
coef_digits = number_of_integer_digits(number.coef)
leading_zeros = abs(number.exp) - coef_digits
exp = -(leading_zeros + 1)
mantissa = %Decimal{number | exp: -coef_digits + 1}
{mantissa, exp}
else
coef_digits = number_of_integer_digits(number.coef)
exp = coef_digits + number.exp - 1
mantissa = %Decimal{number | exp: number.exp - exp}
{mantissa, exp}
end
end
defp between_one_and_minus_one(number) do
(Decimal.cmp(number, @minus_one) == :gt && Decimal.cmp(number, @one) == :lt)
|| Decimal.cmp(number, @one) == :eq
|| Decimal.cmp(number, @minus_one) == :eq
end
@doc """
Calculates the square root of a Decimal number using Newton's method.
* `number` must be a `Decimal`
We convert the Decimal to a float and take its
`:math.sqrt` only to get an initial estimate.
The means typically we are only two iterations from
a solution so the slight hack improves performance
without sacrificing precision.
## Examples
iex> Cldr.Number.Math.sqrt(Decimal.new(9))
#Decimal<3.0>
iex> Cldr.Number.Math.sqrt(Decimal.new(9.869))
#Decimal<3.141496458696078173887197038>
"""
@precision 0.0001
@decimal_precision Decimal.new(@precision)
def sqrt(number, precision \\ @precision)
def sqrt(%Decimal{sign: sign} = number, _precision)
when sign == -1 do
raise ArgumentError, "bad argument in arithmetic expression #{inspect number}"
end
# Get an initial estimate of the sqrt by using the built in `:math.sqrt`
# function. This means typically its only two iterations to get the default
# the sqrt at the specified precision.
def sqrt(%Decimal{} = number, precision)
when is_number(precision) do
initial_estimate = number
|> to_float
|> :math.sqrt
|> Decimal.new
decimal_precision = Decimal.new(precision)
do_sqrt(number, initial_estimate, @decimal_precision, decimal_precision)
end
defp do_sqrt(%Decimal{} = number, %Decimal{} = estimate,
%Decimal{} = old_estimate, %Decimal{} = precision) do
diff = estimate
|> Decimal.sub(old_estimate)
|> Decimal.abs
if Decimal.cmp(diff, old_estimate) == :lt
|| Decimal.cmp(diff, old_estimate) == :eq do
estimate
else
Decimal.div(number, Decimal.mult(@two, estimate))
new_estimate = Decimal.add(Decimal.div(estimate, @two),
Decimal.div(number, Decimal.mult(@two, estimate)))
do_sqrt(number, new_estimate, estimate, precision)
end
end
@doc """
Calculate the nth root of a number.
* `number` is an integer or a Decimal
* `nth` is a positive integer
## Examples
iex> Cldr.Number.Math.root Decimal.new(8), 3
#Decimal<2.0>
iex> Cldr.Number.Math.root Decimal.new(16), 4
#Decimal<2.0>
iex> Cldr.Number.Math.root Decimal.new(27), 3
#Decimal<3.0>
"""
def root(%Decimal{} = number, nth) when is_integer(nth) and nth > 0 do
guess = :math.pow(to_float(number), 1 / nth)
|> Decimal.new
do_root number, Decimal.new(nth), guess
end
def root(number, nth) when is_number(number) and is_integer(nth) and nth > 0 do
guess = :math.pow(number, 1 / nth)
do_root number, nth, guess
end
@root_precision 0.0001
defp do_root(number, nth, root) when is_number(number) do
delta = (1 / nth) * (number / :math.pow(root, nth - 1)) - root
if delta > @root_precision do
do_root(number, nth, root + delta)
else
root
end
end
@decimal_root_precision Decimal.new(@root_precision)
defp do_root(%Decimal{} = number, %Decimal{} = nth, %Decimal{} = root) do
d1 = Decimal.div(@one, nth)
d2 = Decimal.div(number, power(root, Decimal.sub(nth, @one)))
d3 = Decimal.sub(d2, root)
delta = Decimal.mult(d1, d3)
if Decimal.cmp(delta, @decimal_root_precision) == :gt do
do_root(number, nth, Decimal.add(root, delta))
else
root
end
end
end