NumberF.Precision (NumberF v0.3.0)

Copy Markdown View Source

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

approximately_equal(a, b, epsilon \\ 1.0e-10)

Checks if two floating point numbers are approximately equal.

Parameters

  • a: First number
  • b: Second number
  • epsilon: 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

bankers_round(number, precision \\ 2)

Rounds a number using banker's rounding (round to even). This is more statistically unbiased than standard rounding.

Parameters

  • number: The number to round
  • precision: 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

ceiling(number, precision \\ 2)

Rounds a number up to a specified precision (ceiling).

Parameters

  • number: The number to round
  • precision: 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

clamp(value, min, max)

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

custom_round(number, precision, mode \\ :half_up)

Implements a custom rounding function with different modes.

Parameters

  • number: The number to round
  • precision: Number of decimal places
  • mode: 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

floor(number, precision \\ 2)

Rounds a number down to a specified precision (floor).

Parameters

  • number: The number to round
  • precision: 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

precise_format(number, precision \\ 2)

Formats a number with a specified precision, using string manipulation to avoid floating-point rounding issues.

Parameters

  • number: The number to format
  • precision: 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"

round_half_away_from_zero(number, precision \\ 2)

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

round_half_even(number, precision \\ 2)

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

round_to(number, increment \\ 1.0, strategy \\ :nearest)

Rounds a number to a specific increment.

Parameters

  • number: The number to round
  • increment: 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

round_with_precision(number, precision \\ 2)

Rounds a number to a specified number of decimal places.

Parameters

  • number: The number to round
  • precision: 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

safe_divide(numerator, denominator, default \\ 0.0)

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

sanitize_float(value, default \\ 0.0)

Sanitizes a float value to handle special cases like NaN and Infinity.

Parameters

  • value: The value to sanitize
  • default: 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

sign(number)

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

truncate(number, precision \\ 2)

Truncates a number to a specified precision (cuts off digits).

Parameters

  • number: The number to truncate
  • precision: 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