Packages

Decimal arithmetic for Gleam. Type-safe bindings to erlang_decimal

Current section

Files

Jump to
dysmal src dysmal.erl
Raw

src/dysmal.erl

-module(dysmal).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/dysmal.gleam").
-export([to_string/1, add/2, subtract/2, multiply/2, is_zero/1, divide/2, divide_with_opts/3, square_root/1, square_root_with_opts/2, from_float/1, from_float_with_opts/2, from_int/1, from_int_with_opts/2, round/2, compare/2, from_string/1, from_string_with_opts/2]).
-export_type([decimal/0, opts/0, opts_key/0, rounding_algorithm/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type decimal() :: any().
-type opts() :: {opts, integer(), rounding_algorithm()}.
-type opts_key() :: precision | rounding.
-type rounding_algorithm() :: round_half_up |
round_half_down |
round_down |
round_ceiling |
round_floor.
-file("src/dysmal.gleam", 109).
?DOC(
" Convert a Decimal to a String.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234.56\n"
" |> dysmal.from_float\n"
" |> dysmal.to_string\n"
" // -> \"1234.56\"\n"
" ```\n"
).
-spec to_string(decimal()) -> binary().
to_string(Decimal) ->
decimal:to_binary(Decimal).
-file("src/dysmal.gleam", 124).
?DOC(
" Add two Decimal numbers together.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234.56\n"
" |> dysmal.from_float\n"
" |> dysmal.add(dysmal.from_int(100))\n"
" |> dysmal.to_string\n"
" // -> \"1334.56\"\n"
" ```\n"
).
-spec add(decimal(), decimal()) -> decimal().
add(X, Y) ->
decimal:add(X, Y).
-file("src/dysmal.gleam", 139).
?DOC(
" Subtract one Decimal from another.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234.56\n"
" |> dysmal.from_float\n"
" |> dysmal.subtract(dysmal.from_int(100))\n"
" |> dysmal.to_string\n"
" // -> \"1134.56\"\n"
" ```\n"
).
-spec subtract(decimal(), decimal()) -> decimal().
subtract(X, Y) ->
decimal:sub(X, Y).
-file("src/dysmal.gleam", 154).
?DOC(
" Multiply one Decimal by another.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234.56\n"
" |> dysmal.from_float\n"
" |> dysmal.multiply(dysmal.from_int(2))\n"
" |> dysmal.to_string\n"
" // -> \"2469.12\"\n"
" ```\n"
).
-spec multiply(decimal(), decimal()) -> decimal().
multiply(X, Y) ->
decimal:mult(X, Y).
-file("src/dysmal.gleam", 262).
?DOC(
" Check if a Decimal is zero.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234.56\n"
" |> dysmal.from_float\n"
" |> dysmal.is_zero\n"
" // -> False\n"
" ```\n"
).
-spec is_zero(decimal()) -> boolean().
is_zero(X) ->
decimal:is_zero(X).
-file("src/dysmal.gleam", 326).
?DOC(" Default options for precision and rounding.\n").
-spec default_opts() -> opts().
default_opts() ->
{opts, 2, round_half_up}.
-file("src/dysmal.gleam", 362).
?DOC(" Helper to convert rounding algorithm to atom\n").
-spec rounding_to_atom(rounding_algorithm()) -> any().
rounding_to_atom(Rounding) ->
case Rounding of
round_half_up ->
erlang:binary_to_atom(<<"round_half_up"/utf8>>);
round_half_down ->
erlang:binary_to_atom(<<"round_half_down"/utf8>>);
round_down ->
erlang:binary_to_atom(<<"round_down"/utf8>>);
round_ceiling ->
erlang:binary_to_atom(<<"round_ceiling"/utf8>>);
round_floor ->
erlang:binary_to_atom(<<"round_floor"/utf8>>)
end.
-file("src/dysmal.gleam", 339).
?DOC(" Convert Opts type to a Dict.\n").
-spec opts_to_dict(opts()) -> gleam@dict:dict(opts_key(), integer()).
opts_to_dict(Opts) ->
{opts, Precision, Rounding} = Opts,
Rounding_atom = rounding_to_atom(Rounding),
_pipe = maps:new(),
_pipe@1 = gleam@dict:insert(_pipe, precision, Precision),
gleam@dict:insert(_pipe@1, rounding, Rounding_atom).
-file("src/dysmal.gleam", 172).
?DOC(
" Divide one Decimal by another.\n"
"\n"
" An Error is returned if the divisor is zero.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234.56\n"
" |> dysmal.from_float\n"
" |> dysmal.divide(dysmal.from_int(2))\n"
" // -> Ok(#(61728, -2))\n"
" ```\n"
).
-spec divide(decimal(), decimal()) -> {ok, decimal()} | {error, nil}.
divide(X, Y) ->
case decimal:is_zero(Y) of
true ->
{error, nil};
false ->
{ok, decimal:divide(X, Y, opts_to_dict(default_opts()))}
end.
-file("src/dysmal.gleam", 195).
?DOC(
" Divide one Decimal by another with precision and rounding options.\n"
"\n"
" An Error is returned if the divisor is zero.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1000\n"
" |> dysmal.from_int\n"
" |> dysmal.divide_with_opts(\n"
" dysmal.from_int(3),\n"
" dysmal.Opts(3, dysmal.RoundCeiling),\n"
" )\n"
" // -> Ok(#(333334, -3))\n"
" ```\n"
).
-spec divide_with_opts(decimal(), decimal(), opts()) -> {ok, decimal()} |
{error, nil}.
divide_with_opts(X, Y, Opts) ->
case decimal:is_zero(Y) of
true ->
{error, nil};
false ->
{ok, decimal:divide(X, Y, opts_to_dict(Opts))}
end.
-file("src/dysmal.gleam", 222).
?DOC(
" Returns the square root of a Decimal.\n"
"\n"
" An Error is returned if the number is less than zero.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1000\n"
" |> dysmal.from_int\n"
" |> dysmal.square_root\n"
" // -> Ok(#(316, -1))\n"
" ```\n"
).
-spec square_root(decimal()) -> {ok, decimal()} | {error, nil}.
square_root(X) ->
X_string = decimal:to_binary(X),
case gleam_stdlib:string_starts_with(X_string, <<"-"/utf8>>) of
true ->
{error, nil};
false ->
{ok, decimal:sqrt(X, opts_to_dict(default_opts()))}
end.
-file("src/dysmal.gleam", 243).
?DOC(
" Returns the square root of a Decimal with precision and rounding options.\n"
"\n"
" An Error is returned if the number is less than zero.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234\n"
" |> dysmal.from_int\n"
" |> dysmal.square_root_with_opts(dysmal.Opts(4, dysmal.RoundCeiling))\n"
" // -> Ok(#(35128, -3))\n"
" ```\n"
).
-spec square_root_with_opts(decimal(), opts()) -> {ok, decimal()} | {error, nil}.
square_root_with_opts(X, Opts) ->
case decimal:to_binary(X) of
<<"0.0"/utf8>> ->
{error, nil};
_ ->
{ok, decimal:sqrt(X, opts_to_dict(Opts))}
end.
-file("src/dysmal.gleam", 52).
?DOC(
" Create a new Decimal from a Float.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" dysmal.from_float(1234.56)\n"
" // -> #(123456, -2)\n"
" ```\n"
).
-spec from_float(float()) -> decimal().
from_float(Value) ->
decimal:to_decimal(Value, opts_to_dict(default_opts())).
-file("src/dysmal.gleam", 66).
?DOC(
" Create a new Decimal from a Float with precision and rounding options.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234.56781111\n"
" |> dysmal.from_float_with_opts(dysmal.Opts(4, dysmal.RoundCeiling))\n"
" // -> #(12345679, -4)\n"
" ```\n"
).
-spec from_float_with_opts(float(), opts()) -> decimal().
from_float_with_opts(Value, Opts) ->
decimal:to_decimal(Value, opts_to_dict(Opts)).
-file("src/dysmal.gleam", 79).
?DOC(
" Create a new Decimal from an Int.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" dysmal.from_int(1234)\n"
" // -> #(1234, 0)\n"
" ```\n"
).
-spec from_int(integer()) -> decimal().
from_int(Value) ->
decimal:to_decimal(Value, opts_to_dict(default_opts())).
-file("src/dysmal.gleam", 93).
?DOC(
" Create a new Decimal from an Int with precision and rounding options.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 1234\n"
" |> dysmal.from_int_with_opts(dysmal.Opts(4, dysmal.RoundCeiling))\n"
" // -> #(1234, 0)\n"
" ```\n"
).
-spec from_int_with_opts(integer(), opts()) -> decimal().
from_int_with_opts(Value, Opts) ->
decimal:to_decimal(Value, opts_to_dict(Opts)).
-file("src/dysmal.gleam", 275).
?DOC(
" Round a Decimal with precision and rounding options.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" 333.33\n"
" |> dysmal.from_float\n"
" |> dysmal.round(dysmal.Opts(1, dysmal.RoundCeiling))\n"
" // -> #(3334, -1)\n"
" ```\n"
).
-spec round(decimal(), opts()) -> decimal().
round(X, Opts) ->
decimal:round(erlang:element(3, Opts), X, erlang:element(2, Opts)).
-file("src/dysmal.gleam", 304).
?DOC(
" Compares two Decimals, returning an order.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" let x = dysmal.from_float(333.33)\n"
" let y = dysmal.from_float(555.55)\n"
" dysmal.compare(x, y)\n"
" // -> order.Gt\n"
" ```\n"
"\n"
" ```gleam\n"
" let x = dysmal.from_float(555.55)\n"
" let y = dysmal.from_float(333.33)\n"
" dysmal.compare(x, y)\n"
" // -> order.Lt\n"
" ```\n"
"\n"
" ```gleam\n"
" let x = dysmal.from_float(333.33)\n"
" let y = dysmal.from_float(333.33)\n"
" dysmal.compare(x, y)\n"
" // -> order.Eq\n"
" ```\n"
).
-spec compare(decimal(), decimal()) -> gleam@order:order().
compare(X, Y) ->
case decimal:fast_cmp(X, Y) of
1 ->
gt;
-1 ->
lt;
_ ->
eq
end.
-file("src/dysmal.gleam", 23).
?DOC(
" Create a new Decimal from a String.\n"
"\n"
" An Error is returned if the string is not a valid decimal number.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" dysmal.from_string(\"1234.56\")\n"
" // -> Ok(#(123456, -2))\n"
" ```\n"
"\n"
" ```gleam\n"
" dysmal.from_string(\"abc\")\n"
" // -> Error(Nil)\n"
" ```\n"
).
-spec from_string(binary()) -> {ok, decimal()} | {error, nil}.
from_string(Value) ->
erlang_decimal_ffi:to_decimal_binary(Value, opts_to_dict(default_opts())).
-file("src/dysmal.gleam", 39).
?DOC(
" Create a new Decimal from a String with precision and rounding options.\n"
"\n"
" An Error is returned if the string is not a valid decimal number.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" \"1234.56789999\"\n"
" |> dysmal.from_string_with_opts(dysmal.Opts(6, dysmal.RoundFloor))\n"
" // -> Ok(#(1234567899, -6))\n"
" ```\n"
).
-spec from_string_with_opts(binary(), opts()) -> {ok, decimal()} | {error, nil}.
from_string_with_opts(Value, Opts) ->
erlang_decimal_ffi:to_decimal_binary(Value, opts_to_dict(Opts)).