:decimal (FakeDecimal v0.1.2)

Copy Markdown View Source

This library provides a :decimal module that has the same functions (API, interface) as the good^H^H^H^Hbad old erlang_decimal library, but it is just an interface to the Elixir Decimal library.

The old erlang_decimal used 2-tuples with integers in it to represent numbers, while the Elixir decimal uses structs. By using this library, the module :decimal serves as the old API, and the module Decimal as the new one. Unfortunately, the application names clash in the package decimal and erlang_decimal, so you cannot use them at once, but fake_decimal plays nicely with the Elixir decimal.

If there's a legacy application that calls into :decimal, but you also need the Elixir decimal library in your app, you may be able to provide the old API for the legacy app by using fake_decimal.

The functions where rounding semantics matter (round/3, divide/3, sqrt/2, cmp/3, fast_cmp/2, to_binary/1,2) reproduce the original erlang_decimal algorithms exactly: precision counts fractional digits (decimal places, not significant digits), and the round_half_up / round_half_down modes decide based on the first discarded digit only, just like the original.

Parsing and formatting live in :decimal_conv, a direct port of the original's second module, so strings, charlists and floats convert exactly as they did before — including raising badarg on malformed input.

Like the original, the opts maps of to_decimal/2, divide/3, sqrt/2 and cmp/3 must carry both :precision and :rounding whenever the call actually needs them; a partial map raises FunctionClauseError.

Deviations from erlang_decimal

  • add/2, sub/2 and mult/2 results are normalized: add({1, 0}, {9, 0}) returns {1, 1} where the original returns {10, 0}. The two tuples are numerically equal (cmp/3 returns 0 and reduce/1 maps both to the same tuple), but code that pattern-matches exact tuples may see a different — equivalent — representation.

  • Wherever the original accepts only {coef, exp} tuples, this module also accepts the old {sign, coef, exp} format, Elixir Decimal structs, integers, floats, strings and charlists. The original raises FunctionClauseError for those; accepting them cannot break code that worked against the original.

Summary

Types

decimal()

@type decimal() :: {integer(), integer()}

opts()

@type opts() :: %{
  optional(:precision) => non_neg_integer(),
  optional(:rounding) => rounding_algorithm()
}

rounding_algorithm()

@type rounding_algorithm() ::
  :round_floor
  | :round_ceiling
  | :round_half_up
  | :round_half_down
  | :round_down

Functions

abs(a)

@spec abs(value()) :: decimal()

add(a, b)

@spec add(value(), value()) :: decimal()

cmp(a, b, opts)

@spec cmp(value(), value(), opts()) :: -1 | 0 | 1

divide(a, b, opts)

@spec divide(value(), value(), opts()) :: decimal()

fast_cmp(a, b)

@spec fast_cmp(value(), value()) :: -1 | 0 | 1

is_zero(a)

@spec is_zero(value()) :: boolean()

minus(a)

@spec minus(value()) :: decimal()

mult(a, b)

@spec mult(value(), value()) :: decimal()

reduce(a)

@spec reduce(value()) :: decimal()

round(mode, num, precision \\ 0)

@spec round(rounding_algorithm(), value(), non_neg_integer()) :: decimal()

sqrt(a, opts)

@spec sqrt(value(), opts()) :: decimal() | no_return()

sub(a, b)

@spec sub(value(), value()) :: decimal()

to_binary(a)

@spec to_binary(value()) :: binary()

to_binary(a, opts)

@spec to_binary(value(), :decimal_conv.binary_opts()) :: binary()

to_decimal(value)

@spec to_decimal(value()) :: decimal()

to_decimal(value, arg2)

@spec to_decimal(value(), opts()) :: decimal()

to_decimal(base, exp, opts)

@spec to_decimal(integer(), integer(), opts()) :: decimal()