Functions for handling numerical precision issues and implementing various rounding strategies.
Summary
Functions
Checks if two floating point numbers are approximately equal.
Rounds a number using banker's rounding (round to even). This is more statistically unbiased than standard rounding.
Rounds a number up to a specified precision (ceiling).
Constrains a value to a range.
Implements a custom rounding function with different modes.
Rounds a number down to a specified precision (floor).
Formats a number with a specified precision, using string manipulation to avoid floating-point rounding issues.
Rounds half away from zero.
Rounds half to even. An alias for bankers_round/2, named for discoverability.
Rounds a number to a specific increment.
Rounds a number to a specified number of decimal places.
Divides, returning default instead of raising when the denominator is zero.
Sanitizes a float value to handle special cases like NaN and Infinity.
Returns -1, 0 or 1 according to the sign of the number.
Truncates a number to a specified precision (cuts off digits).
Functions
Checks if two floating point numbers are approximately equal.
Parameters
a: First numberb: Second numberepsilon: Maximum allowed difference (default: 1.0e-10)
Examples
iex> NumberF.Precision.approximately_equal(0.1 + 0.2, 0.3)
true
iex> NumberF.Precision.approximately_equal(0.1, 0.2)
false
Rounds a number using banker's rounding (round to even). This is more statistically unbiased than standard rounding.
Parameters
number: The number to roundprecision: Number of decimal places (default: 2)
Examples
iex> NumberF.Precision.bankers_round(2.5, 0)
2.0
iex> NumberF.Precision.bankers_round(3.5, 0)
4.0
iex> NumberF.Precision.bankers_round(2.125, 2)
2.12
iex> NumberF.Precision.bankers_round(2.135, 2)
2.14
Rounds a number up to a specified precision (ceiling).
Parameters
number: The number to roundprecision: Number of decimal places (default: 2)
Examples
iex> NumberF.Precision.ceiling(3.14159, 2)
3.15
iex> NumberF.Precision.ceiling(3.14159, 1)
3.2
Constrains a value to a range.
The single most reached-for utility that was missing.
Examples
iex> NumberF.Precision.clamp(15, 0, 10)
10
iex> NumberF.Precision.clamp(-5, 0, 10)
0
iex> NumberF.Precision.clamp(5, 0, 10)
5
Implements a custom rounding function with different modes.
Parameters
number: The number to roundprecision: Number of decimal placesmode: Rounding mode (default: :half_up)- :half_up - Round to nearest, 0.5 rounds up (standard)
- :half_down - Round to nearest, 0.5 rounds down
- :half_even - Round to nearest, 0.5 rounds to even (banker's)
- :ceiling - Always round up
- :floor - Always round down
- :truncate - Always truncate (toward zero)
Examples
iex> NumberF.Precision.custom_round(2.5, 0, :half_up)
3.0
iex> NumberF.Precision.custom_round(2.5, 0, :half_down)
2.0
iex> NumberF.Precision.custom_round(2.5, 0, :half_even)
2.0
iex> NumberF.Precision.custom_round(3.5, 0, :half_even)
4.0
Rounds a number down to a specified precision (floor).
Parameters
number: The number to roundprecision: Number of decimal places (default: 2)
Examples
iex> NumberF.Precision.floor(3.14159, 2)
3.14
iex> NumberF.Precision.floor(3.14159, 1)
3.1
Formats a number with a specified precision, using string manipulation to avoid floating-point rounding issues.
Parameters
number: The number to formatprecision: Number of decimal places (default: 2)
Examples
iex> NumberF.Precision.precise_format(3.14159, 2)
"3.14"
iex> NumberF.Precision.precise_format(3.14159, 4)
"3.1416"
Rounds half away from zero.
Precision
This is the rounding most people mean by "round half up", and it differs from
Elixir's round/1 for negatives: round(-2.5) is -3 in Elixir but many
languages return -2. It also differs from bankers_round/2, which rounds
half to even to avoid systematic bias. Use this for presentation and
bankers_round/2 for money that accumulates.
Examples
iex> NumberF.Precision.round_half_away_from_zero(2.5, 0)
3.0
iex> NumberF.Precision.round_half_away_from_zero(-2.5, 0)
-3.0
Rounds half to even. An alias for bankers_round/2, named for discoverability.
Examples
iex> NumberF.Precision.round_half_even(2.5, 0)
2.0
iex> NumberF.Precision.round_half_even(3.5, 0)
4.0
Rounds a number to a specific increment.
Parameters
number: The number to roundincrement: The increment to round to (default: 1.0)strategy: The rounding strategy (:nearest, :up, :down, or :bankers)
Examples
iex> NumberF.Precision.round_to(3.14159, 0.05, :nearest)
3.15
iex> NumberF.Precision.round_to(3.14159, 0.1, :up)
3.2
iex> NumberF.Precision.round_to(3.14159, 0.1, :down)
3.1
Rounds a number to a specified number of decimal places.
Parameters
number: The number to roundprecision: Number of decimal places (default: 2)
Examples
iex> NumberF.Precision.round_with_precision(3.14159, 2)
3.14
iex> NumberF.Precision.round_with_precision(3.14159, 4)
3.1416
Divides, returning default instead of raising when the denominator is zero.
Edge cases
Use this at boundaries where a zero denominator is expected and meaningful — a rate over an empty period, for instance. Where zero indicates a bug, let the division raise instead of hiding it behind a default.
Examples
iex> NumberF.Precision.safe_divide(10, 4)
2.5
iex> NumberF.Precision.safe_divide(10, 0)
0.0
iex> NumberF.Precision.safe_divide(10, 0, :infinity)
:infinity
Sanitizes a float value to handle special cases like NaN and Infinity.
Parameters
value: The value to sanitizedefault: Default value to return for invalid numbers (default: 0.0)
Examples
iex> NumberF.Precision.sanitize_float(:nan, 0.0)
0.0
iex> NumberF.Precision.sanitize_float(:infinity, 0.0)
0.0
iex> NumberF.Precision.sanitize_float(3.14, 0.0)
3.14
Returns -1, 0 or 1 according to the sign of the number.
Examples
iex> NumberF.Precision.sign(-4.2)
-1
iex> NumberF.Precision.sign(0)
0
iex> NumberF.Precision.sign(17)
1
Truncates a number to a specified precision (cuts off digits).
Parameters
number: The number to truncateprecision: Number of decimal places (default: 2)
Examples
iex> NumberF.Precision.truncate(3.14159, 2)
3.14
iex> NumberF.Precision.truncate(-3.14159, 2)
-3.14