Packages

Arbitrary precision decimal arithmetic for Gleam. A pure Gleam implementation of Elixir's Decimal library.

Current section

Files

Jump to
dee src dee@decimal.erl
Raw

src/dee@decimal.erl

-module(dee@decimal).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/dee/decimal.gleam").
-export([default_context/0, context/2, sign/1, coefficient/1, exponent/1, zero/0, one/0, infinity/0, neg_infinity/0, nan/0, from_int/1, from_parts/3, from_string/1, parse/1, from_float/1, absolute_value/1, negate/1, add/2, subtract/2, multiply/2, compare/2, eq/2, gt/2, lt/2, gte/2, lte/2, max/2, min/2, to_string/1, to_string_scientific/1, to_string_raw/1, to_string_xsd/1, to_int/1, to_float/1, scale/1, round/3, normalize/1, apply_context/2, sqrt_with_context/2, square_root/1, divide_with_context/3, divide/2, is_nan/1, is_inf/1, is_positive/1, is_negative/1, compare_with_threshold/3, eq_with_threshold/3, is_zero/1, is_integer/1, divide_integer/2, remainder/2, divide_remainder/2]).
-export_type([sign/0, decimal/0, rounding_mode/0, context/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.
?MODULEDOC(
" Arbitrary precision decimal arithmetic for Gleam.\n"
"\n"
" A pure Gleam implementation of\n"
" [Elixir's Decimal](https://hexdocs.pm/decimal) library, providing the\n"
" `Decimal` opaque type with arithmetic, comparison, conversion, and\n"
" rounding operations. Works identically on both Erlang and JavaScript\n"
" targets using a single codebase.\n"
"\n"
" A decimal number is represented as `sign * coefficient * 10 ^ exponent`\n"
" where the coefficient is an arbitrary precision integer. This avoids the\n"
" rounding errors inherent to floating-point arithmetic.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" // Floating point: 0.1 + 0.2 = 0.30000000000000004\n"
" // Decimal: 0.1 + 0.2 = 0.3\n"
" let assert Ok(a) = from_string(\"0.1\")\n"
" let assert Ok(b) = from_string(\"0.2\")\n"
" to_string(add(a, b))\n"
" // -> \"0.3\"\n"
" ```\n"
"\n"
" ```gleam\n"
" let assert Ok(price) = from_string(\"19.99\")\n"
" let assert Ok(tax_rate) = from_string(\"0.08\")\n"
" let tax = multiply(price, tax_rate)\n"
" to_string(tax)\n"
" // -> \"1.5992\"\n"
" ```\n"
"\n"
" ## Elixir Decimal Compatibility\n"
"\n"
" This library follows Elixir's Decimal as its reference implementation.\n"
" The API, arithmetic behavior, rounding modes, and special value handling\n"
" are designed to match. Code ported between the two libraries should\n"
" produce identical results.\n"
"\n"
" Both libraries use the same mathematical representation internally\n"
" (`sign * coefficient * 10^exponent`) with the same semantics for\n"
" `NaN`, `Infinity`, and signed zero. The default context matches\n"
" Elixir's: 28 digits of precision with `HalfUp` rounding.\n"
"\n"
" While the runtime representations differ (Gleam opaque type vs Elixir\n"
" struct), converting between them on the BEAM is straightforward via\n"
" `to_string`/`from_string` or by mapping the component fields through\n"
" `from_parts`, `sign`, and `exponent`.\n"
).
-type sign() :: positive | negative.
-opaque decimal() :: {decimal, sign(), dee@internal:coefficient(), integer()}.
-type rounding_mode() :: down |
up |
ceiling |
floor |
half_up |
half_even |
half_down.
-type context() :: {context, integer(), rounding_mode()}.
-file("src/dee/decimal.gleam", 108).
?DOC(
" The default context with precision 28 and HalfUp rounding.\n"
" Matches Elixir Decimal's default and IEEE decimal128.\n"
).
-spec default_context() -> context().
default_context() ->
{context, 28, half_up}.
-file("src/dee/decimal.gleam", 120).
?DOC(
" Creates a new context with the given precision and rounding mode.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" context(10, HalfEven)\n"
" // -> Context(precision: 10, rounding: HalfEven)\n"
" ```\n"
).
-spec context(integer(), rounding_mode()) -> context().
context(Precision, Rounding) ->
{context, Precision, Rounding}.
-file("src/dee/decimal.gleam", 137).
?DOC(
" Returns the sign of the decimal number.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" sign(from_int(42))\n"
" // -> Positive\n"
"\n"
" sign(from_int(-7))\n"
" // -> Negative\n"
" ```\n"
).
-spec sign(decimal()) -> sign().
sign(D) ->
erlang:element(2, D).
-file("src/dee/decimal.gleam", 145).
?DOC(false).
-spec coefficient(decimal()) -> dee@internal:coefficient().
coefficient(D) ->
erlang:element(3, D).
-file("src/dee/decimal.gleam", 158).
?DOC(
" Returns the exponent of the decimal number.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"1.23\")\n"
" exponent(d)\n"
" // -> -2\n"
" ```\n"
).
-spec exponent(decimal()) -> integer().
exponent(D) ->
erlang:element(4, D).
-file("src/dee/decimal.gleam", 165).
?DOC(" Decimal zero (0).\n").
-spec zero() -> decimal().
zero() ->
{decimal, positive, {finite, bigi_ffi:zero()}, 0}.
-file("src/dee/decimal.gleam", 170).
?DOC(" Decimal one (1).\n").
-spec one() -> decimal().
one() ->
{decimal, positive, {finite, bigi_ffi:one()}, 0}.
-file("src/dee/decimal.gleam", 175).
?DOC(" Positive infinity().\n").
-spec infinity() -> decimal().
infinity() ->
{decimal, positive, infinity, 0}.
-file("src/dee/decimal.gleam", 180).
?DOC(" Negative infinity().\n").
-spec neg_infinity() -> decimal().
neg_infinity() ->
{decimal, negative, infinity, 0}.
-file("src/dee/decimal.gleam", 185).
?DOC(" NaN (Not a Number).\n").
-spec nan() -> decimal().
nan() ->
{decimal, positive, na_n, 0}.
-file("src/dee/decimal.gleam", 194).
-spec big_pow10(integer()) -> bigi:big_int().
big_pow10(N) ->
case N of
0 ->
bigi_ffi:one();
1 ->
bigi_ffi:ten();
2 ->
bigi_ffi:from(100);
3 ->
bigi_ffi:from(1000);
4 ->
bigi_ffi:from(10000);
5 ->
bigi_ffi:from(100000);
6 ->
bigi_ffi:from(1000000);
7 ->
bigi_ffi:from(10000000);
8 ->
bigi_ffi:from(100000000);
9 ->
bigi_ffi:from(1000000000);
_ when N > 0 ->
Result@1 = case bigi_ffi:power(bigi_ffi:ten(), bigi_ffi:from(N)) of
{ok, Result} -> Result;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"dee/decimal"/utf8>>,
function => <<"big_pow10"/utf8>>,
line => 207,
value => _assert_fail,
start => 5685,
'end' => 5749,
pattern_start => 5696,
pattern_end => 5706})
end,
Result@1;
_ ->
bigi_ffi:one()
end.
-file("src/dee/decimal.gleam", 216).
-spec big_is_zero(bigi:big_int()) -> boolean().
big_is_zero(N) ->
bigi_ffi:compare(N, bigi_ffi:zero()) =:= eq.
-file("src/dee/decimal.gleam", 221).
-spec big_two() -> bigi:big_int().
big_two() ->
bigi_ffi:from(2).
-file("src/dee/decimal.gleam", 239).
?DOC(
" Creates a new decimal number from an integer. The decimal number will\n"
" be created exactly as specified with all digits kept.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" from_int(42) |> to_string\n"
" // -> \"42\"\n"
"\n"
" from_int(-7) |> to_string\n"
" // -> \"-7\"\n"
" ```\n"
).
-spec from_int(integer()) -> decimal().
from_int(Value) ->
Big_value = bigi_ffi:from(Value),
case Value < 0 of
true ->
{decimal, negative, {finite, bigi_ffi:negate(Big_value)}, 0};
false ->
{decimal, positive, {finite, Big_value}, 0}
end.
-file("src/dee/decimal.gleam", 262).
?DOC(
" Creates a new decimal number from the sign, coefficient, and exponent\n"
" such that the number will be: `sign * coefficient * 10 ^ exponent`.\n"
"\n"
" The coefficient is stored as its absolute value regardless of input.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" from_parts(Positive, 42, 0) |> to_string\n"
" // -> \"42\"\n"
"\n"
" from_parts(Negative, 12345, -2) |> to_string\n"
" // -> \"-123.45\"\n"
" ```\n"
).
-spec from_parts(sign(), integer(), integer()) -> decimal().
from_parts(S, Coef, Exp) ->
Abs_coef = case Coef < 0 of
true ->
bigi_ffi:from(- Coef);
false ->
bigi_ffi:from(Coef)
end,
{decimal, S, {finite, Abs_coef}, Exp}.
-file("src/dee/decimal.gleam", 479).
-spec is_digit(binary()) -> boolean().
is_digit(C) ->
case C of
<<"0"/utf8>> ->
true;
<<"1"/utf8>> ->
true;
<<"2"/utf8>> ->
true;
<<"3"/utf8>> ->
true;
<<"4"/utf8>> ->
true;
<<"5"/utf8>> ->
true;
<<"6"/utf8>> ->
true;
<<"7"/utf8>> ->
true;
<<"8"/utf8>> ->
true;
<<"9"/utf8>> ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 463).
-spec take_digits_acc(list(binary()), list(binary())) -> {list(binary()),
list(binary())}.
take_digits_acc(Chars, Acc) ->
case Chars of
[] ->
{lists:reverse(Acc), []};
[C | Rest] ->
case is_digit(C) of
true ->
take_digits_acc(Rest, [C | Acc]);
false ->
{lists:reverse(Acc), Chars}
end
end.
-file("src/dee/decimal.gleam", 459).
-spec take_digits(list(binary())) -> {list(binary()), list(binary())}.
take_digits(Chars) ->
take_digits_acc(Chars, []).
-file("src/dee/decimal.gleam", 496).
-spec trim_leading_zeros_list(list(binary())) -> list(binary()).
trim_leading_zeros_list(Chars) ->
case Chars of
[] ->
[];
[<<"0"/utf8>>] ->
[<<"0"/utf8>>];
[<<"0"/utf8>> | Rest] ->
trim_leading_zeros_list(Rest);
_ ->
Chars
end.
-file("src/dee/decimal.gleam", 487).
-spec trim_leading_zeros(binary()) -> binary().
trim_leading_zeros(S) ->
Chars = gleam@string:to_graphemes(S),
Trimmed = trim_leading_zeros_list(Chars),
case Trimmed of
[] ->
<<"0"/utf8>>;
_ ->
erlang:list_to_binary(Trimmed)
end.
-file("src/dee/decimal.gleam", 441).
-spec build_decimal(sign(), binary(), integer(), list(binary())) -> {ok,
{decimal(), binary()}} |
{error, nil}.
build_decimal(S, Digits, Exp, Remaining) ->
Trimmed_digits = trim_leading_zeros(Digits),
case bigi_ffi:from_string(Trimmed_digits) of
{ok, Coef} ->
Decimal = {decimal, S, {finite, Coef}, Exp},
{ok, {Decimal, erlang:list_to_binary(Remaining)}};
{error, _} ->
{error, nil}
end.
-file("src/dee/decimal.gleam", 407).
-spec parse_with_exponent(sign(), binary(), integer(), list(binary())) -> {ok,
{decimal(), binary()}} |
{error, nil}.
parse_with_exponent(S, Digits, Base_exp, Chars) ->
case Chars of
[<<"e"/utf8>> | Rest] ->
{Exp_sign, After_sign} = case Rest of
[<<"-"/utf8>> | R] ->
{-1, R};
[<<"+"/utf8>> | R@1] ->
{1, R@1};
_ ->
{1, Rest}
end,
{Exp_digits, After_exp} = take_digits(After_sign),
case Exp_digits of
[] ->
build_decimal(S, Digits, Base_exp, Chars);
_ ->
gleam@result:'try'(
begin
_pipe = gleam_stdlib:parse_int(
erlang:list_to_binary(Exp_digits)
),
gleam@result:replace_error(_pipe, nil)
end,
fun(Exp_val) ->
Total_exp = Base_exp + (Exp_sign * Exp_val),
build_decimal(S, Digits, Total_exp, After_exp)
end
)
end;
[<<"E"/utf8>> | Rest] ->
{Exp_sign, After_sign} = case Rest of
[<<"-"/utf8>> | R] ->
{-1, R};
[<<"+"/utf8>> | R@1] ->
{1, R@1};
_ ->
{1, Rest}
end,
{Exp_digits, After_exp} = take_digits(After_sign),
case Exp_digits of
[] ->
build_decimal(S, Digits, Base_exp, Chars);
_ ->
gleam@result:'try'(
begin
_pipe = gleam_stdlib:parse_int(
erlang:list_to_binary(Exp_digits)
),
gleam@result:replace_error(_pipe, nil)
end,
fun(Exp_val) ->
Total_exp = Base_exp + (Exp_sign * Exp_val),
build_decimal(S, Digits, Total_exp, After_exp)
end
)
end;
_ ->
build_decimal(S, Digits, Base_exp, Chars)
end.
-file("src/dee/decimal.gleam", 361).
-spec parse_number(sign(), list(binary())) -> {ok, {decimal(), binary()}} |
{error, nil}.
parse_number(S, Chars) ->
{Int_digits, After_int} = take_digits(Chars),
case {Int_digits, After_int} of
{[], [<<"."/utf8>> | After_dot]} ->
{Frac_digits, After_frac} = take_digits(After_dot),
case Frac_digits of
[] ->
{error, nil};
_ ->
All_digits = erlang:list_to_binary(
[<<"0"/utf8>>, erlang:list_to_binary(Frac_digits)]
),
Exp = - erlang:length(Frac_digits),
parse_with_exponent(S, All_digits, Exp, After_frac)
end;
{[], _} ->
{error, nil};
{_, [<<"."/utf8>> | After_dot@1]} ->
{Frac_digits@1, After_frac@1} = take_digits(After_dot@1),
case Frac_digits@1 of
[] ->
All_digits@1 = erlang:list_to_binary(Int_digits),
parse_with_exponent(S, All_digits@1, 0, After_dot@1);
_ ->
All_digits@2 = erlang:list_to_binary(
[erlang:list_to_binary(Int_digits),
erlang:list_to_binary(Frac_digits@1)]
),
Exp@1 = - erlang:length(Frac_digits@1),
parse_with_exponent(S, All_digits@2, Exp@1, After_frac@1)
end;
{_, _} ->
All_digits@3 = erlang:list_to_binary(Int_digits),
parse_with_exponent(S, All_digits@3, 0, After_int)
end.
-file("src/dee/decimal.gleam", 343).
-spec parse_numeric_string(binary()) -> {ok, {decimal(), binary()}} |
{error, nil}.
parse_numeric_string(Input) ->
Chars = gleam@string:to_graphemes(Input),
case Chars of
[] ->
{error, nil};
[First | Rest] ->
{S, Remaining_chars} = case First of
<<"-"/utf8>> ->
{negative, Rest};
<<"+"/utf8>> ->
{positive, Rest};
_ ->
{positive, Chars}
end,
parse_number(S, Remaining_chars)
end.
-file("src/dee/decimal.gleam", 323).
-spec parse_decimal_string(binary()) -> {ok, {decimal(), binary()}} |
{error, nil}.
parse_decimal_string(Input) ->
case gleam@string:is_empty(Input) of
true ->
{error, nil};
false ->
Lower = string:lowercase(Input),
case Lower of
<<"infinity"/utf8>> ->
{ok, {infinity(), <<""/utf8>>}};
<<"inf"/utf8>> ->
{ok, {infinity(), <<""/utf8>>}};
<<"-infinity"/utf8>> ->
{ok, {neg_infinity(), <<""/utf8>>}};
<<"-inf"/utf8>> ->
{ok, {neg_infinity(), <<""/utf8>>}};
<<"+infinity"/utf8>> ->
{ok, {infinity(), <<""/utf8>>}};
<<"+inf"/utf8>> ->
{ok, {infinity(), <<""/utf8>>}};
<<"nan"/utf8>> ->
{ok, {nan(), <<""/utf8>>}};
<<"-nan"/utf8>> ->
{ok, {{decimal, negative, na_n, 0}, <<""/utf8>>}};
<<"+nan"/utf8>> ->
{ok, {nan(), <<""/utf8>>}};
_ ->
parse_numeric_string(Input)
end
end.
-file("src/dee/decimal.gleam", 289).
?DOC(
" Parses a string into a decimal number. A decimal number will always be\n"
" created exactly as specified with all digits kept.\n"
"\n"
" Accepts integers, decimals, scientific notation, `\"Infinity\"`, `\"-Infinity\"`,\n"
" and `\"NaN\"`. Returns `Error` with the invalid input string on failure.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" from_string(\"3.14\")\n"
" // -> Ok(...)\n"
"\n"
" from_string(\"-Infinity\")\n"
" // -> Ok(...)\n"
"\n"
" from_string(\"bad\")\n"
" // -> Error(\"bad\")\n"
" ```\n"
).
-spec from_string(binary()) -> {ok, decimal()} | {error, binary()}.
from_string(Input) ->
Trimmed = gleam@string:trim(Input),
case parse_decimal_string(Trimmed) of
{ok, {Decimal, Rest}} ->
case gleam@string:is_empty(Rest) of
true ->
{ok, Decimal};
false ->
{error, Input}
end;
{error, _} ->
{error, Input}
end.
-file("src/dee/decimal.gleam", 315).
?DOC(
" Parses a string into a decimal, returning the decimal and the remaining\n"
" unparsed portion of the string. If parsing fails, returns `Error` with\n"
" the original input string.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" parse(\"3.14rest\")\n"
" // -> Ok(#(..., \"rest\"))\n"
"\n"
" parse(\"bad\")\n"
" // -> Error(\"bad\")\n"
" ```\n"
).
-spec parse(binary()) -> {ok, {decimal(), binary()}} | {error, binary()}.
parse(Input) ->
case parse_decimal_string(Input) of
{ok, Result} ->
{ok, Result};
{error, _} ->
{error, Input}
end.
-file("src/dee/decimal.gleam", 516).
?DOC(
" Creates a new decimal number from a floating point number.\n"
"\n"
" Note that due to float representation, the result may not be exactly\n"
" the value you expect. Use `from_string` for exact decimal values.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" from_float(3.14) |> to_string\n"
" // -> \"3.14\"\n"
" ```\n"
).
-spec from_float(float()) -> decimal().
from_float(Value) ->
Str = gleam_stdlib:float_to_string(Value),
case from_string(Str) of
{ok, D} ->
D;
{error, _} ->
zero()
end.
-file("src/dee/decimal.gleam", 540).
?DOC(
" Returns the absolute value of the given number. Sets the number's sign\n"
" to positive.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" absolute_value(from_int(1)) |> to_string\n"
" // -> \"1\"\n"
"\n"
" absolute_value(from_int(-1)) |> to_string\n"
" // -> \"1\"\n"
" ```\n"
).
-spec absolute_value(decimal()) -> decimal().
absolute_value(A) ->
case erlang:element(3, A) of
na_n ->
nan();
_ ->
{decimal, positive, erlang:element(3, A), erlang:element(4, A)}
end.
-file("src/dee/decimal.gleam", 558).
?DOC(
" Negates the given number.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" negate(from_int(1)) |> to_string\n"
" // -> \"-1\"\n"
"\n"
" negate(from_int(-1)) |> to_string\n"
" // -> \"1\"\n"
" ```\n"
).
-spec negate(decimal()) -> decimal().
negate(A) ->
case erlang:element(3, A) of
na_n ->
nan();
_ ->
New_sign = case erlang:element(2, A) of
positive ->
negative;
negative ->
positive
end,
{decimal, New_sign, erlang:element(3, A), erlang:element(4, A)}
end.
-file("src/dee/decimal.gleam", 603).
-spec add_finite(
sign(),
bigi:big_int(),
integer(),
sign(),
bigi:big_int(),
integer()
) -> decimal().
add_finite(Sa, Ca, Ea, Sb, Cb, Eb) ->
Min_exp = gleam@int:min(Ea, Eb),
Aligned_ca = bigi_ffi:multiply(Ca, big_pow10(Ea - Min_exp)),
Aligned_cb = bigi_ffi:multiply(Cb, big_pow10(Eb - Min_exp)),
Signed_a = case Sa of
positive ->
Aligned_ca;
negative ->
bigi_ffi:negate(Aligned_ca)
end,
Signed_b = case Sb of
positive ->
Aligned_cb;
negative ->
bigi_ffi:negate(Aligned_cb)
end,
Sum = bigi_ffi:add(Signed_a, Signed_b),
case bigi_ffi:compare(Sum, bigi_ffi:zero()) of
lt ->
{decimal, negative, {finite, bigi_ffi:negate(Sum)}, Min_exp};
eq ->
Result_sign = case Sa =:= Sb of
true ->
Sa;
false ->
positive
end,
{decimal, Result_sign, {finite, Sum}, Min_exp};
gt ->
{decimal, positive, {finite, Sum}, Min_exp}
end.
-file("src/dee/decimal.gleam", 581).
?DOC(
" Adds two numbers together.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(a) = from_string(\"0.1\")\n"
" let assert Ok(b) = from_string(\"0.2\")\n"
" add(a, b) |> to_string\n"
" // -> \"0.3\"\n"
" ```\n"
).
-spec add(decimal(), decimal()) -> decimal().
add(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, _} ->
nan();
{_, na_n} ->
nan();
{infinity, infinity} ->
case erlang:element(2, A) =:= erlang:element(2, B) of
true ->
A;
false ->
nan()
end;
{infinity, _} ->
A;
{_, infinity} ->
B;
{{finite, Ca}, {finite, Cb}} ->
add_finite(
erlang:element(2, A),
Ca,
erlang:element(4, A),
erlang:element(2, B),
Cb,
erlang:element(4, B)
)
end.
-file("src/dee/decimal.gleam", 653).
?DOC(
" Subtracts the second number from the first. Equivalent to `add` when the\n"
" second number's sign is negated.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(a) = from_string(\"1\")\n"
" let assert Ok(b) = from_string(\"0.1\")\n"
" subtract(a, b) |> to_string\n"
" // -> \"0.9\"\n"
" ```\n"
).
-spec subtract(decimal(), decimal()) -> decimal().
subtract(A, B) ->
add(A, negate(B)).
-file("src/dee/decimal.gleam", 702).
-spec mult_signs(sign(), sign()) -> sign().
mult_signs(A, B) ->
case {A, B} of
{positive, positive} ->
positive;
{negative, negative} ->
positive;
{_, _} ->
negative
end.
-file("src/dee/decimal.gleam", 666).
?DOC(
" Multiplies two numbers.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(a) = from_string(\"0.5\")\n"
" multiply(a, from_int(3)) |> to_string\n"
" // -> \"1.5\"\n"
" ```\n"
).
-spec multiply(decimal(), decimal()) -> decimal().
multiply(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, _} ->
nan();
{_, na_n} ->
nan();
{infinity, {finite, Cb}} ->
case big_is_zero(Cb) of
true ->
nan();
false ->
Result_sign = mult_signs(
erlang:element(2, A),
erlang:element(2, B)
),
{decimal, Result_sign, infinity, 0}
end;
{{finite, Ca}, infinity} ->
case big_is_zero(Ca) of
true ->
nan();
false ->
Result_sign@1 = mult_signs(
erlang:element(2, A),
erlang:element(2, B)
),
{decimal, Result_sign@1, infinity, 0}
end;
{infinity, infinity} ->
Result_sign@2 = mult_signs(
erlang:element(2, A),
erlang:element(2, B)
),
{decimal, Result_sign@2, infinity, 0};
{{finite, Ca@1}, {finite, Cb@1}} ->
Result_sign@3 = mult_signs(
erlang:element(2, A),
erlang:element(2, B)
),
Result_coef = bigi_ffi:multiply(Ca@1, Cb@1),
Result_exp = erlang:element(4, A) + erlang:element(4, B),
{decimal, Result_sign@3, {finite, Result_coef}, Result_exp}
end.
-file("src/dee/decimal.gleam", 819).
-spec sqrt_loop(bigi:big_int(), bigi:big_int()) -> bigi:big_int().
sqrt_loop(Guess, Coef) ->
Quotient = bigi_ffi:divide(Coef, Guess),
case bigi_ffi:compare(Guess, Quotient) of
lt ->
Guess;
eq ->
Guess;
gt ->
New_guess = bigi_ffi:divide(
bigi_ffi:add(Guess, Quotient),
big_two()
),
sqrt_loop(New_guess, Coef)
end.
-file("src/dee/decimal.gleam", 906).
-spec div_adjust(bigi:big_int(), bigi:big_int(), integer()) -> {bigi:big_int(),
bigi:big_int(),
integer()}.
div_adjust(Ca, Cb, Adjust) ->
case bigi_ffi:compare(Ca, Cb) of
lt ->
div_adjust(bigi_ffi:multiply(Ca, bigi_ffi:ten()), Cb, Adjust + 1);
_ ->
Cb_times_10 = bigi_ffi:multiply(Cb, bigi_ffi:ten()),
case bigi_ffi:compare(Ca, Cb_times_10) of
lt ->
{Ca, Cb, Adjust};
eq ->
{Ca, Cb, Adjust};
gt ->
div_adjust(Ca, Cb_times_10, Adjust - 1)
end
end.
-file("src/dee/decimal.gleam", 922).
-spec div_calc(
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
integer(),
bigi:big_int()
) -> {bigi:big_int(), integer(), bigi:big_int()}.
div_calc(Ca, Cb, Coef, Adjust, Prec10) ->
Digit = bigi_ffi:divide(Ca, Cb),
Remainder = bigi_ffi:remainder(Ca, Cb),
New_coef = bigi_ffi:add(Coef, Digit),
case big_is_zero(Remainder) andalso (Adjust >= 0) of
true ->
{New_coef, Adjust, bigi_ffi:zero()};
false ->
case bigi_ffi:compare(New_coef, Prec10) of
gt ->
{New_coef, Adjust, Remainder};
eq ->
{New_coef, Adjust, Remainder};
lt ->
div_calc(
bigi_ffi:multiply(Remainder, bigi_ffi:ten()),
Cb,
bigi_ffi:multiply(New_coef, bigi_ffi:ten()),
Adjust + 1,
Prec10
)
end
end.
-file("src/dee/decimal.gleam", 964).
-spec normalize_coef_exp_loop(bigi:big_int(), integer()) -> {bigi:big_int(),
integer()}.
normalize_coef_exp_loop(Coef, Exp) ->
case big_is_zero(bigi_ffi:remainder(Coef, bigi_ffi:ten())) of
true ->
normalize_coef_exp_loop(
bigi_ffi:divide(Coef, bigi_ffi:ten()),
Exp + 1
);
false ->
{Coef, Exp}
end.
-file("src/dee/decimal.gleam", 957).
-spec normalize_coef_exp(bigi:big_int(), integer()) -> {bigi:big_int(),
integer()}.
normalize_coef_exp(Coef, Exp) ->
case big_is_zero(Coef) of
true ->
{bigi_ffi:zero(), 0};
false ->
normalize_coef_exp_loop(Coef, Exp)
end.
-file("src/dee/decimal.gleam", 1072).
-spec compare_same_sign(bigi:big_int(), integer(), bigi:big_int(), integer()) -> gleam@order:order().
compare_same_sign(Ca, Ea, Cb, Eb) ->
case Ea =:= Eb of
true ->
bigi_ffi:compare(Ca, Cb);
false ->
Min_exp = gleam@int:min(Ea, Eb),
Aligned_a = bigi_ffi:multiply(Ca, big_pow10(Ea - Min_exp)),
Aligned_b = bigi_ffi:multiply(Cb, big_pow10(Eb - Min_exp)),
bigi_ffi:compare(Aligned_a, Aligned_b)
end.
-file("src/dee/decimal.gleam", 1088).
-spec reverse_order(gleam@order:order()) -> gleam@order:order().
reverse_order(Ord) ->
case Ord of
lt ->
gt;
gt ->
lt;
eq ->
eq
end.
-file("src/dee/decimal.gleam", 1027).
-spec compare_finite(
sign(),
bigi:big_int(),
integer(),
sign(),
bigi:big_int(),
integer()
) -> gleam@order:order().
compare_finite(Sa, Ca, Ea, Sb, Cb, Eb) ->
Ca_zero = big_is_zero(Ca),
Cb_zero = big_is_zero(Cb),
case {Ca_zero, Cb_zero} of
{true, true} ->
eq;
{true, false} ->
case Sb of
positive ->
lt;
negative ->
gt
end;
{false, true} ->
case Sa of
positive ->
gt;
negative ->
lt
end;
{false, false} ->
case {Sa, Sb} of
{positive, negative} ->
gt;
{negative, positive} ->
lt;
{positive, positive} ->
compare_same_sign(Ca, Ea, Cb, Eb);
{negative, negative} ->
reverse_order(compare_same_sign(Ca, Ea, Cb, Eb))
end
end.
-file("src/dee/decimal.gleam", 996).
-spec compare_ordered(decimal(), decimal()) -> gleam@order:order().
compare_ordered(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{infinity, infinity} ->
case {erlang:element(2, A), erlang:element(2, B)} of
{positive, positive} ->
eq;
{negative, negative} ->
eq;
{positive, negative} ->
gt;
{negative, positive} ->
lt
end;
{infinity, _} ->
case erlang:element(2, A) of
positive ->
gt;
negative ->
lt
end;
{_, infinity} ->
case erlang:element(2, B) of
positive ->
lt;
negative ->
gt
end;
{{finite, Ca}, {finite, Cb}} ->
compare_finite(
erlang:element(2, A),
Ca,
erlang:element(4, A),
erlang:element(2, B),
Cb,
erlang:element(4, B)
);
{_, _} ->
eq
end.
-file("src/dee/decimal.gleam", 986).
?DOC(
" Compares two numbers numerically. Returns `Ok(order.Lt)`, `Ok(order.Eq)`,\n"
" or `Ok(order.Gt)`. Returns `Error(Nil)` if either operand is `NaN` since\n"
" `NaN` is not ordered.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" compare(from_int(1), from_int(2))\n"
" // -> Ok(order.Lt)\n"
"\n"
" compare(nan(), from_int(1))\n"
" // -> Error(Nil)\n"
" ```\n"
).
-spec compare(decimal(), decimal()) -> {ok, gleam@order:order()} | {error, nil}.
compare(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, _} ->
{error, nil};
{_, na_n} ->
{error, nil};
{_, _} ->
{ok, compare_ordered(A, B)}
end.
-file("src/dee/decimal.gleam", 1107).
?DOC(
" Compares two numbers numerically and returns `True` if they are equal,\n"
" otherwise `False`. Numerical equality means `1.0` equals `1.00`.\n"
" `NaN` is not equal to itself.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(a) = from_string(\"1.0\")\n"
" eq(a, from_int(1))\n"
" // -> True\n"
" ```\n"
).
-spec eq(decimal(), decimal()) -> boolean().
eq(A, B) ->
case compare(A, B) of
{ok, eq} ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 1116).
?DOC(
" Compares two numbers numerically and returns `True` if the first\n"
" argument is greater than the second, otherwise `False`.\n"
).
-spec gt(decimal(), decimal()) -> boolean().
gt(A, B) ->
case compare(A, B) of
{ok, gt} ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 1125).
?DOC(
" Compares two numbers numerically and returns `True` if the first\n"
" argument is less than the second, otherwise `False`.\n"
).
-spec lt(decimal(), decimal()) -> boolean().
lt(A, B) ->
case compare(A, B) of
{ok, lt} ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 1134).
?DOC(
" Compares two numbers numerically and returns `True` if the first\n"
" argument is greater than or equal to the second, otherwise `False`.\n"
).
-spec gte(decimal(), decimal()) -> boolean().
gte(A, B) ->
case compare(A, B) of
{ok, gt} ->
true;
{ok, eq} ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 1143).
?DOC(
" Compares two numbers numerically and returns `True` if the first\n"
" argument is less than or equal to the second, otherwise `False`.\n"
).
-spec lte(decimal(), decimal()) -> boolean().
lte(A, B) ->
case compare(A, B) of
{ok, lt} ->
true;
{ok, eq} ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 1178).
-spec max_tiebreak(decimal(), decimal()) -> decimal().
max_tiebreak(A, B) ->
gleam@bool:guard(
erlang:element(2, A) /= erlang:element(2, B),
case erlang:element(2, A) of
positive ->
A;
negative ->
B
end,
fun() -> case erlang:element(2, A) of
positive ->
case erlang:element(4, A) > erlang:element(4, B) of
true ->
A;
false ->
B
end;
negative ->
case erlang:element(4, A) < erlang:element(4, B) of
true ->
A;
false ->
B
end
end end
).
-file("src/dee/decimal.gleam", 1163).
?DOC(
" Compares two values numerically and returns the maximum. Unlike most\n"
" other functions, if a number is `NaN` the result will be the other number.\n"
" If both are `NaN`, returns `NaN`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" max(from_int(1), from_int(2)) |> to_string\n"
" // -> \"2\"\n"
"\n"
" max(from_int(1), nan()) |> to_string\n"
" // -> \"1\"\n"
" ```\n"
).
-spec max(decimal(), decimal()) -> decimal().
max(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, na_n} ->
nan();
{na_n, _} ->
B;
{_, na_n} ->
A;
{_, _} ->
case compare_ordered(A, B) of
gt ->
A;
lt ->
B;
eq ->
max_tiebreak(A, B)
end
end.
-file("src/dee/decimal.gleam", 1227).
-spec min_tiebreak(decimal(), decimal()) -> decimal().
min_tiebreak(A, B) ->
gleam@bool:guard(
erlang:element(2, A) /= erlang:element(2, B),
case erlang:element(2, A) of
negative ->
A;
positive ->
B
end,
fun() -> case erlang:element(2, A) of
positive ->
case erlang:element(4, A) < erlang:element(4, B) of
true ->
A;
false ->
B
end;
negative ->
case erlang:element(4, A) > erlang:element(4, B) of
true ->
A;
false ->
B
end
end end
).
-file("src/dee/decimal.gleam", 1212).
?DOC(
" Compares two values numerically and returns the minimum. Unlike most\n"
" other functions, if a number is `NaN` the result will be the other number.\n"
" If both are `NaN`, returns `NaN`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" min(from_int(1), from_int(2)) |> to_string\n"
" // -> \"1\"\n"
"\n"
" min(from_int(1), nan()) |> to_string\n"
" // -> \"1\"\n"
" ```\n"
).
-spec min(decimal(), decimal()) -> decimal().
min(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, na_n} ->
nan();
{na_n, _} ->
B;
{_, na_n} ->
A;
{_, _} ->
case compare_ordered(A, B) of
lt ->
A;
gt ->
B;
eq ->
min_tiebreak(A, B)
end
end.
-file("src/dee/decimal.gleam", 1352).
-spec format_zero(binary(), integer()) -> binary().
format_zero(Sign_str, Exp) ->
case Exp of
0 ->
erlang:list_to_binary([Sign_str, <<"0"/utf8>>]);
_ when Exp < 0 ->
Zeros = gleam@string:repeat(<<"0"/utf8>>, - Exp),
erlang:list_to_binary([Sign_str, <<"0."/utf8>>, Zeros]);
_ ->
erlang:list_to_binary(
[Sign_str, <<"0E+"/utf8>>, erlang:integer_to_binary(Exp)]
)
end.
-file("src/dee/decimal.gleam", 1363).
-spec format_decimal_notation(binary(), binary(), integer(), integer()) -> binary().
format_decimal_notation(Sign_str, Coef_str, Coef_len, Exp) ->
Decimal_pos = Coef_len + Exp,
case Decimal_pos =< 0 of
true ->
Leading_zeros = gleam@string:repeat(<<"0"/utf8>>, - Decimal_pos),
erlang:list_to_binary(
[Sign_str, <<"0."/utf8>>, Leading_zeros, Coef_str]
);
false ->
gleam@bool:guard(
Exp =:= 0,
erlang:list_to_binary([Sign_str, Coef_str]),
fun() ->
Int_part = gleam@string:slice(Coef_str, 0, Decimal_pos),
Frac_part = gleam@string:slice(
Coef_str,
Decimal_pos,
Coef_len - Decimal_pos
),
erlang:list_to_binary(
[Sign_str, Int_part, <<"."/utf8>>, Frac_part]
)
end
)
end.
-file("src/dee/decimal.gleam", 1385).
-spec format_e_notation(binary(), binary(), integer(), integer()) -> binary().
format_e_notation(Sign_str, Coef_str, Coef_len, Adjusted_exp) ->
First_digit = gleam@string:slice(Coef_str, 0, 1),
Rest_digits = gleam@string:slice(Coef_str, 1, Coef_len - 1),
Exp_sign = case Adjusted_exp >= 0 of
true ->
<<"+"/utf8>>;
false ->
<<""/utf8>>
end,
case gleam@string:is_empty(Rest_digits) of
true ->
erlang:list_to_binary(
[Sign_str,
First_digit,
<<"E"/utf8>>,
Exp_sign,
erlang:integer_to_binary(Adjusted_exp)]
);
false ->
erlang:list_to_binary(
[Sign_str,
First_digit,
<<"."/utf8>>,
Rest_digits,
<<"E"/utf8>>,
Exp_sign,
erlang:integer_to_binary(Adjusted_exp)]
)
end.
-file("src/dee/decimal.gleam", 1334).
-spec to_string_finite(sign(), bigi:big_int(), integer()) -> binary().
to_string_finite(S, Coef, Exp) ->
Sign_str = case S of
positive ->
<<""/utf8>>;
negative ->
<<"-"/utf8>>
end,
gleam@bool:guard(
big_is_zero(Coef),
format_zero(Sign_str, Exp),
fun() ->
Coef_str = erlang:integer_to_binary(Coef),
Coef_len = string:length(Coef_str),
Adjusted_exp = (Exp + Coef_len) - 1,
case (Exp =< 0) andalso (Adjusted_exp >= -6) of
true ->
format_decimal_notation(Sign_str, Coef_str, Coef_len, Exp);
false ->
format_e_notation(
Sign_str,
Coef_str,
Coef_len,
Adjusted_exp
)
end
end
).
-file("src/dee/decimal.gleam", 1318).
?DOC(
" Converts the given number to its string representation. Uses decimal\n"
" notation when possible, scientific notation for very large or very small\n"
" exponents.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"1.00\")\n"
" to_string(d)\n"
" // -> \"1.00\"\n"
"\n"
" to_string(from_int(42))\n"
" // -> \"42\"\n"
" ```\n"
).
-spec to_string(decimal()) -> binary().
to_string(D) ->
case erlang:element(3, D) of
na_n ->
<<"NaN"/utf8>>;
infinity ->
case erlang:element(2, D) of
positive ->
<<"Infinity"/utf8>>;
negative ->
<<"-Infinity"/utf8>>
end;
{finite, Coef} ->
to_string_finite(erlang:element(2, D), Coef, erlang:element(4, D))
end.
-file("src/dee/decimal.gleam", 1442).
-spec to_string_scientific_finite(sign(), bigi:big_int(), integer()) -> binary().
to_string_scientific_finite(S, Coef, Exp) ->
Sign_str = case S of
positive ->
<<""/utf8>>;
negative ->
<<"-"/utf8>>
end,
case big_is_zero(Coef) of
true ->
erlang:list_to_binary([Sign_str, <<"0E+0"/utf8>>]);
false ->
{Norm_coef, Norm_exp} = normalize_coef_exp(Coef, Exp),
Coef_str = erlang:integer_to_binary(Norm_coef),
Coef_len = string:length(Coef_str),
First_digit = gleam@string:slice(Coef_str, 0, 1),
Rest_digits = gleam@string:slice(Coef_str, 1, Coef_len - 1),
Adjusted_exp = (Norm_exp + Coef_len) - 1,
Exp_sign = case Adjusted_exp >= 0 of
true ->
<<"+"/utf8>>;
false ->
<<""/utf8>>
end,
case gleam@string:is_empty(Rest_digits) of
true ->
erlang:list_to_binary(
[Sign_str,
First_digit,
<<"E"/utf8>>,
Exp_sign,
erlang:integer_to_binary(Adjusted_exp)]
);
false ->
erlang:list_to_binary(
[Sign_str,
First_digit,
<<"."/utf8>>,
Rest_digits,
<<"E"/utf8>>,
Exp_sign,
erlang:integer_to_binary(Adjusted_exp)]
)
end
end.
-file("src/dee/decimal.gleam", 1428).
?DOC(
" Converts the given number to a string in scientific notation.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"123.45\")\n"
" to_string_scientific(d)\n"
" // -> \"1.2345E+2\"\n"
" ```\n"
).
-spec to_string_scientific(decimal()) -> binary().
to_string_scientific(D) ->
case erlang:element(3, D) of
na_n ->
<<"NaN"/utf8>>;
infinity ->
case erlang:element(2, D) of
positive ->
<<"Infinity"/utf8>>;
negative ->
<<"-Infinity"/utf8>>
end;
{finite, Coef} ->
to_string_scientific_finite(
erlang:element(2, D),
Coef,
erlang:element(4, D)
)
end.
-file("src/dee/decimal.gleam", 1500).
?DOC(
" Converts the given number to a string in raw format showing the\n"
" coefficient and exponent directly: `\"12345E-2\"`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"123.45\")\n"
" to_string_raw(d)\n"
" // -> \"12345E-2\"\n"
" ```\n"
).
-spec to_string_raw(decimal()) -> binary().
to_string_raw(D) ->
case erlang:element(3, D) of
na_n ->
<<"NaN"/utf8>>;
infinity ->
case erlang:element(2, D) of
positive ->
<<"Infinity"/utf8>>;
negative ->
<<"-Infinity"/utf8>>
end;
{finite, Coef} ->
Sign_str = case erlang:element(2, D) of
positive ->
<<""/utf8>>;
negative ->
<<"-"/utf8>>
end,
Exp_sign = case erlang:element(4, D) >= 0 of
true ->
<<"+"/utf8>>;
false ->
<<""/utf8>>
end,
erlang:list_to_binary(
[Sign_str,
erlang:integer_to_binary(Coef),
<<"E"/utf8>>,
Exp_sign,
erlang:integer_to_binary(erlang:element(4, D))]
)
end.
-file("src/dee/decimal.gleam", 1591).
-spec expand_positive_exp(bigi:big_int(), integer()) -> {bigi:big_int(),
integer()}.
expand_positive_exp(Coef, Exp) ->
case Exp > 0 of
true ->
expand_positive_exp(
bigi_ffi:multiply(Coef, bigi_ffi:ten()),
Exp - 1
);
false ->
{Coef, Exp}
end.
-file("src/dee/decimal.gleam", 1599).
-spec remove_trailing_zeros_xsd(bigi:big_int(), integer()) -> {bigi:big_int(),
integer()}.
remove_trailing_zeros_xsd(Coef, Exp) ->
case Exp >= -1 of
true ->
{Coef, Exp};
false ->
case big_is_zero(bigi_ffi:remainder(Coef, bigi_ffi:ten())) of
true ->
remove_trailing_zeros_xsd(
bigi_ffi:divide(Coef, bigi_ffi:ten()),
Exp + 1
);
false ->
{Coef, Exp}
end
end.
-file("src/dee/decimal.gleam", 1571).
-spec canonical_xsd(bigi:big_int(), integer()) -> {bigi:big_int(), integer()}.
canonical_xsd(Coef, Exp) ->
case big_is_zero(Coef) of
true ->
{bigi_ffi:zero(), -1};
false ->
{C, E} = expand_positive_exp(Coef, Exp),
case E >= 0 of
true ->
{bigi_ffi:multiply(C, bigi_ffi:ten()), -1};
false ->
remove_trailing_zeros_xsd(C, E)
end
end.
-file("src/dee/decimal.gleam", 1543).
?DOC(
" Converts the given number to a string in XSD canonical format.\n"
" Integers always include `\".0\"`, trailing zeros are removed (keeping at\n"
" least one decimal digit), and scientific notation is never used.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" to_string_xsd(from_int(42))\n"
" // -> \"42.0\"\n"
"\n"
" let assert Ok(d) = from_string(\"1.00\")\n"
" to_string_xsd(d)\n"
" // -> \"1.0\"\n"
" ```\n"
).
-spec to_string_xsd(decimal()) -> binary().
to_string_xsd(D) ->
case erlang:element(3, D) of
na_n ->
case erlang:element(2, D) of
positive ->
<<"NaN"/utf8>>;
negative ->
<<"-NaN"/utf8>>
end;
infinity ->
case erlang:element(2, D) of
positive ->
<<"Infinity"/utf8>>;
negative ->
<<"-Infinity"/utf8>>
end;
{finite, Coef} ->
{Xsd_coef, Xsd_exp} = canonical_xsd(Coef, erlang:element(4, D)),
Sign_str = case erlang:element(2, D) of
positive ->
<<""/utf8>>;
negative ->
<<"-"/utf8>>
end,
gleam@bool:guard(
big_is_zero(Xsd_coef),
erlang:list_to_binary([Sign_str, <<"0.0"/utf8>>]),
fun() ->
to_string_finite(erlang:element(2, D), Xsd_coef, Xsd_exp)
end
)
end.
-file("src/dee/decimal.gleam", 1636).
-spec to_int_finite(sign(), bigi:big_int(), integer()) -> {ok, integer()} |
{error, nil}.
to_int_finite(S, Coef, Exp) ->
gleam@result:'try'(case Exp >= 0 of
true ->
{ok, bigi_ffi:multiply(Coef, big_pow10(Exp))};
false ->
Divisor = big_pow10(- Exp),
case big_is_zero(bigi_ffi:remainder(Coef, Divisor)) of
true ->
{ok, bigi_ffi:divide(Coef, Divisor)};
false ->
{error, nil}
end
end, fun(Value) ->
Signed = case S of
positive ->
Value;
negative ->
bigi_ffi:negate(Value)
end,
_pipe = bigi_ffi:to(Signed),
gleam@result:replace_error(_pipe, nil)
end).
-file("src/dee/decimal.gleam", 1629).
?DOC(
" Returns the decimal converted to an integer. Fails when the decimal has\n"
" a fractional part, is `NaN`, or is `Infinity`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" to_int(from_int(42))\n"
" // -> Ok(42)\n"
"\n"
" let assert Ok(d) = from_string(\"1.00\")\n"
" to_int(d)\n"
" // -> Ok(1)\n"
"\n"
" let assert Ok(d) = from_string(\"1.5\")\n"
" to_int(d)\n"
" // -> Error(Nil)\n"
" ```\n"
).
-spec to_int(decimal()) -> {ok, integer()} | {error, nil}.
to_int(D) ->
case erlang:element(3, D) of
na_n ->
{error, nil};
infinity ->
{error, nil};
{finite, Coef} ->
to_int_finite(erlang:element(2, D), Coef, erlang:element(4, D))
end.
-file("src/dee/decimal.gleam", 1665).
?DOC(
" Returns the decimal converted to a float. The returned float may have\n"
" lower precision than the decimal. Returns `Error(Nil)` for `NaN` and\n"
" `Infinity` since these are not representable as floats on all targets.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"1.5\")\n"
" to_float(d)\n"
" // -> Ok(1.5)\n"
" ```\n"
).
-spec to_float(decimal()) -> {ok, float()} | {error, nil}.
to_float(D) ->
case erlang:element(3, D) of
na_n ->
{error, nil};
infinity ->
{error, nil};
{finite, _} ->
Str = to_string(D),
case gleam_stdlib:parse_float(Str) of
{ok, F} ->
{ok, F};
{error, _} ->
_pipe = gleam_stdlib:parse_int(Str),
_pipe@1 = gleam@result:map(_pipe, fun erlang:float/1),
gleam@result:replace_error(_pipe@1, nil)
end
end.
-file("src/dee/decimal.gleam", 1694).
?DOC(
" Returns the scale of the decimal. A decimal's scale is the number of\n"
" digits after the decimal point.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" scale(from_int(42))\n"
" // -> 0\n"
"\n"
" let assert Ok(d) = from_string(\"99.12345\")\n"
" scale(d)\n"
" // -> 5\n"
" ```\n"
).
-spec scale(decimal()) -> integer().
scale(D) ->
case erlang:element(4, D) < 0 of
true ->
- erlang:element(4, D);
false ->
0
end.
-file("src/dee/decimal.gleam", 1725).
-spec round_coef(
bigi:big_int(),
bigi:big_int(),
bigi:big_int(),
sign(),
rounding_mode()
) -> bigi:big_int().
round_coef(Quotient, Remainder, Divisor, S, Mode) ->
gleam@bool:guard(
big_is_zero(Remainder),
Quotient,
fun() ->
Half_divisor = bigi_ffi:divide(Divisor, big_two()),
Divisor_even = big_is_zero(bigi_ffi:remainder(Divisor, big_two())),
At_midpoint = (bigi_ffi:compare(Remainder, Half_divisor) =:= eq)
andalso Divisor_even,
Double_rem = bigi_ffi:multiply(Remainder, big_two()),
Should_round_up = case Mode of
down ->
false;
up ->
true;
ceiling ->
S =:= positive;
floor ->
S =:= negative;
half_up ->
bigi_ffi:compare(Double_rem, Divisor) /= lt;
half_down ->
bigi_ffi:compare(Double_rem, Divisor) =:= gt;
half_even ->
case At_midpoint of
true ->
not big_is_zero(
bigi_ffi:remainder(Quotient, big_two())
);
false ->
bigi_ffi:compare(Double_rem, Divisor) =:= gt
end
end,
case Should_round_up of
true ->
bigi_ffi:add(Quotient, bigi_ffi:one());
false ->
Quotient
end
end
).
-file("src/dee/decimal.gleam", 1761).
-spec round_finite(
sign(),
bigi:big_int(),
integer(),
integer(),
rounding_mode()
) -> decimal().
round_finite(S, Coef, Exp, Places, Mode) ->
Target_exp = - Places,
gleam@bool:guard(
Exp >= Target_exp,
begin
Pad = Exp - Target_exp,
{decimal,
S,
{finite, bigi_ffi:multiply(Coef, big_pow10(Pad))},
Target_exp}
end,
fun() ->
Shift = Target_exp - Exp,
Divisor = big_pow10(Shift),
Quotient = bigi_ffi:divide(Coef, Divisor),
Remainder = bigi_ffi:subtract(
Coef,
bigi_ffi:multiply(Quotient, Divisor)
),
Final_coef = round_coef(Quotient, Remainder, Divisor, S, Mode),
{decimal, S, {finite, Final_coef}, Target_exp}
end
).
-file("src/dee/decimal.gleam", 1716).
?DOC(
" Rounds the given number to the specified number of decimal places with\n"
" the given rounding mode.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"1.234\")\n"
" round(d, 1, HalfUp) |> to_string\n"
" // -> \"1.2\"\n"
"\n"
" round(d, 0, HalfUp) |> to_string\n"
" // -> \"1\"\n"
" ```\n"
).
-spec round(decimal(), integer(), rounding_mode()) -> decimal().
round(D, Places, Mode) ->
case erlang:element(3, D) of
na_n ->
nan();
infinity ->
D;
{finite, Coef} ->
round_finite(
erlang:element(2, D),
Coef,
erlang:element(4, D),
Places,
Mode
)
end.
-file("src/dee/decimal.gleam", 1803).
?DOC(
" Normalizes the given decimal by removing trailing zeros from the\n"
" coefficient while keeping the number numerically equivalent by\n"
" increasing the exponent.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"1.00\")\n"
" normalize(d) |> to_string\n"
" // -> \"1\"\n"
"\n"
" let assert Ok(d) = from_string(\"1.01\")\n"
" normalize(d) |> to_string\n"
" // -> \"1.01\"\n"
" ```\n"
).
-spec normalize(decimal()) -> decimal().
normalize(D) ->
case erlang:element(3, D) of
na_n ->
nan();
infinity ->
D;
{finite, Coef} ->
{Norm_coef, Norm_exp} = normalize_coef_exp(
Coef,
erlang:element(4, D)
),
{decimal, erlang:element(2, D), {finite, Norm_coef}, Norm_exp}
end.
-file("src/dee/decimal.gleam", 1853).
-spec count_digits_acc(bigi:big_int(), integer()) -> integer().
count_digits_acc(N, Acc) ->
case bigi_ffi:compare(N, bigi_ffi:zero()) of
lt ->
case Acc =:= 0 of
true ->
1;
false ->
Acc
end;
eq ->
case Acc =:= 0 of
true ->
1;
false ->
Acc
end;
gt ->
count_digits_acc(bigi_ffi:divide(N, bigi_ffi:ten()), Acc + 1)
end.
-file("src/dee/decimal.gleam", 1849).
-spec count_digits(bigi:big_int()) -> integer().
count_digits(N) ->
count_digits_acc(N, 0).
-file("src/dee/decimal.gleam", 1829).
?DOC(
" Applies the given context to a decimal, rounding to the specified\n"
" precision (significant digits) using the specified rounding mode.\n"
" Special values (`NaN`, `Infinity`) pass through unchanged.\n"
"\n"
" Use `default_context()` for standard precision (28 digits, HalfUp).\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" apply_context(from_int(123), context(2, HalfUp)) |> to_string\n"
" // -> \"1.2E+2\"\n"
"\n"
" apply_context(from_int(5), default_context()) |> to_string\n"
" // -> \"5\"\n"
" ```\n"
).
-spec apply_context(decimal(), context()) -> decimal().
apply_context(D, Ctx) ->
case erlang:element(3, D) of
na_n ->
D;
infinity ->
D;
{finite, Coef} ->
gleam@bool:guard(
big_is_zero(Coef),
D,
fun() ->
Digits = count_digits(Coef),
gleam@bool:guard(
Digits =< erlang:element(2, Ctx),
D,
fun() ->
Shift = Digits - erlang:element(2, Ctx),
Divisor = big_pow10(Shift),
Quotient = bigi_ffi:divide(Coef, Divisor),
Remainder = bigi_ffi:subtract(
Coef,
bigi_ffi:multiply(Quotient, Divisor)
),
Final_coef = round_coef(
Quotient,
Remainder,
Divisor,
erlang:element(2, D),
erlang:element(3, Ctx)
),
{decimal,
erlang:element(2, D),
{finite, Final_coef},
erlang:element(4, D) + Shift}
end
)
end
)
end.
-file("src/dee/decimal.gleam", 749).
-spec sqrt_finite(bigi:big_int(), integer(), context()) -> decimal().
sqrt_finite(Coef, Exp, Ctx) ->
Precision = erlang:element(2, Ctx) + 1,
Num_digits = count_digits(Coef),
{Adjusted_coef, Shift} = case (Exp rem 2) =:= 0 of
true ->
Extra = Precision - ((Num_digits + 1) div 2),
{Coef, Extra};
false ->
Extra@1 = Precision - ((Num_digits div 2) + 1),
{bigi_ffi:multiply(Coef, bigi_ffi:ten()), Extra@1}
end,
{Scaled_coef, Shift_exact} = case Shift >= 0 of
true ->
{bigi_ffi:multiply(Adjusted_coef, big_pow10(Shift * 2)), true};
false ->
Divisor = big_pow10(- Shift * 2),
{bigi_ffi:divide(Adjusted_coef, Divisor),
big_is_zero(bigi_ffi:remainder(Adjusted_coef, Divisor))}
end,
Initial_guess = big_pow10(Precision + 1),
Root = sqrt_loop(Initial_guess, Scaled_coef),
Is_exact = Shift_exact andalso (bigi_ffi:compare(
bigi_ffi:multiply(Root, Root),
Scaled_coef
)
=:= eq),
Preferred_exp = case (Exp < 0) andalso ((Exp rem 2) /= 0) of
true ->
(Exp - 1) div 2;
false ->
Exp div 2
end,
case Is_exact of
true ->
Result_coef = case Shift >= 0 of
true ->
bigi_ffi:divide(Root, big_pow10(Shift));
false ->
bigi_ffi:multiply(Root, big_pow10(- Shift))
end,
Result = {decimal, positive, {finite, Result_coef}, Preferred_exp},
apply_context(Result, Ctx);
false ->
Result_exp = Preferred_exp - Shift,
Result@1 = {decimal, positive, {finite, Root}, Result_exp},
apply_context(Result@1, Ctx)
end.
-file("src/dee/decimal.gleam", 729).
?DOC(
" Square root with explicit context.\n"
" Uses Newton's (Babylonian) method with integer arithmetic.\n"
" Returns NaN for negative inputs, zero for zero, infinity() for infinity().\n"
).
-spec sqrt_with_context(decimal(), context()) -> decimal().
sqrt_with_context(D, Ctx) ->
case erlang:element(3, D) of
na_n ->
nan();
infinity ->
case erlang:element(2, D) of
positive ->
infinity();
negative ->
nan()
end;
{finite, Coef} ->
gleam@bool:guard(
big_is_zero(Coef),
D,
fun() -> case erlang:element(2, D) of
negative ->
nan();
positive ->
sqrt_finite(Coef, erlang:element(4, D), Ctx)
end end
)
end.
-file("src/dee/decimal.gleam", 722).
?DOC(
" Finds the square root of the given number using the default context\n"
" (precision 28, HalfUp rounding).\n"
"\n"
" Returns `NaN` for negative inputs, zero for zero, and infinity() for infinity().\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"100\")\n"
" square_root(d) |> to_string\n"
" // -> \"10\"\n"
" ```\n"
).
-spec square_root(decimal()) -> decimal().
square_root(D) ->
sqrt_with_context(D, default_context()).
-file("src/dee/decimal.gleam", 880).
-spec div_finite(
sign(),
bigi:big_int(),
integer(),
sign(),
bigi:big_int(),
integer(),
context()
) -> decimal().
div_finite(Sa, Ca, Ea, Sb, Cb, Eb, Ctx) ->
Result_sign = mult_signs(Sa, Sb),
{Adj_ca, Adj_cb, Adjust} = div_adjust(Ca, Cb, 0),
Prec10 = big_pow10(erlang:element(2, Ctx)),
{Coef, Final_adjust, _} = div_calc(
Adj_ca,
Adj_cb,
bigi_ffi:zero(),
Adjust,
Prec10
),
Result_exp = (Ea - Eb) - Final_adjust,
Result = {decimal, Result_sign, {finite, Coef}, Result_exp},
apply_context(Result, Ctx).
-file("src/dee/decimal.gleam", 853).
?DOC(
" Divides two numbers using the given context for precision and rounding.\n"
" Division by zero returns `NaN`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let ctx = context(10, HalfEven)\n"
" divide_with_context(from_int(1), from_int(3), ctx) |> to_string\n"
" // -> \"0.3333333333\"\n"
" ```\n"
).
-spec divide_with_context(decimal(), decimal(), context()) -> decimal().
divide_with_context(A, B, Ctx) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, _} ->
nan();
{_, na_n} ->
nan();
{infinity, infinity} ->
nan();
{_, {finite, Cb}} ->
gleam@bool:guard(
big_is_zero(Cb),
nan(),
fun() ->
Result_sign = mult_signs(
erlang:element(2, A),
erlang:element(2, B)
),
case erlang:element(3, A) of
infinity ->
{decimal, Result_sign, infinity, 0};
{finite, Ca} ->
gleam@bool:guard(
big_is_zero(Ca),
{decimal,
Result_sign,
{finite, bigi_ffi:zero()},
0},
fun() ->
div_finite(
erlang:element(2, A),
Ca,
erlang:element(4, A),
erlang:element(2, B),
Cb,
erlang:element(4, B),
Ctx
)
end
);
_ ->
nan()
end
end
);
{_, infinity} ->
Result_sign@1 = mult_signs(
erlang:element(2, A),
erlang:element(2, B)
),
{decimal, Result_sign@1, {finite, bigi_ffi:zero()}, 0}
end.
-file("src/dee/decimal.gleam", 839).
?DOC(
" Divides two numbers using the default context (precision 28, HalfUp\n"
" rounding). Division by zero returns `NaN`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" divide(from_int(3), from_int(4)) |> to_string\n"
" // -> \"0.75\"\n"
" ```\n"
).
-spec divide(decimal(), decimal()) -> decimal().
divide(A, B) ->
divide_with_context(A, B, default_context()).
-file("src/dee/decimal.gleam", 1878).
?DOC(
" Returns `True` if the number is `NaN`, otherwise `False`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" is_nan(nan())\n"
" // -> True\n"
"\n"
" is_nan(from_int(42))\n"
" // -> False\n"
" ```\n"
).
-spec is_nan(decimal()) -> boolean().
is_nan(D) ->
case erlang:element(3, D) of
na_n ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 1897).
?DOC(
" Returns `True` if the number is positive or negative infinity(),\n"
" otherwise `False`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" is_inf(infinity())\n"
" // -> True\n"
"\n"
" is_inf(from_int(1))\n"
" // -> False\n"
" ```\n"
).
-spec is_inf(decimal()) -> boolean().
is_inf(D) ->
case erlang:element(3, D) of
infinity ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 1916).
?DOC(
" Returns `True` if the given number is positive, otherwise `False`.\n"
" Returns `False` for zero, `NaN`, and `Infinity`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" is_positive(from_int(42))\n"
" // -> True\n"
"\n"
" is_positive(from_int(0))\n"
" // -> False\n"
" ```\n"
).
-spec is_positive(decimal()) -> boolean().
is_positive(D) ->
case erlang:element(3, D) of
na_n ->
false;
infinity ->
false;
{finite, Coef} ->
(erlang:element(2, D) =:= positive) andalso (bigi_ffi:compare(
Coef,
bigi_ffi:zero()
)
=:= gt)
end.
-file("src/dee/decimal.gleam", 1935).
?DOC(
" Returns `True` if the given number is negative, otherwise `False`.\n"
" Returns `False` for zero, `NaN`, and `Infinity`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" is_negative(from_int(-42))\n"
" // -> True\n"
"\n"
" is_negative(from_int(0))\n"
" // -> False\n"
" ```\n"
).
-spec is_negative(decimal()) -> boolean().
is_negative(D) ->
case erlang:element(3, D) of
na_n ->
false;
infinity ->
false;
{finite, Coef} ->
(erlang:element(2, D) =:= negative) andalso (bigi_ffi:compare(
Coef,
bigi_ffi:zero()
)
=:= gt)
end.
-file("src/dee/decimal.gleam", 1261).
?DOC(
" Compares two numbers numerically with a threshold tolerance. If the\n"
" second number is within the range of `a - threshold` to `a + threshold`,\n"
" the numbers are considered equal. Returns `Error(Nil)` for negative\n"
" thresholds or `NaN` operands.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(a) = from_string(\"1.1\")\n"
" let assert Ok(t) = from_string(\"0.2\")\n"
" compare_with_threshold(a, from_int(1), t)\n"
" // -> Ok(order.Eq)\n"
" ```\n"
).
-spec compare_with_threshold(decimal(), decimal(), decimal()) -> {ok,
gleam@order:order()} |
{error, nil}.
compare_with_threshold(A, B, Threshold) ->
gleam@bool:guard(
is_negative(Threshold),
{error, nil},
fun() ->
Upper = add(A, Threshold),
Lower = subtract(A, Threshold),
case {compare(Upper, B), compare(Lower, B)} of
{{ok, Upper_cmp}, {ok, Lower_cmp}} ->
B_lte_upper = (Upper_cmp =:= gt) orelse (Upper_cmp =:= eq),
B_gte_lower = (Lower_cmp =:= lt) orelse (Lower_cmp =:= eq),
gleam@bool:guard(
B_lte_upper andalso B_gte_lower,
{ok, eq},
fun() -> case Upper_cmp of
lt ->
{ok, lt};
_ ->
{ok, gt}
end end
);
{_, _} ->
{error, nil}
end
end
).
-file("src/dee/decimal.gleam", 1295).
?DOC(
" Tests if two numbers are equal within a threshold tolerance. If the\n"
" second number is within the range of `a - threshold` to `a + threshold`,\n"
" returns `True`. Returns `False` for negative thresholds or `NaN` operands.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(a) = from_string(\"1.2\")\n"
" let assert Ok(t) = from_string(\"0.2\")\n"
" eq_with_threshold(a, from_int(1), t)\n"
" // -> True\n"
" ```\n"
).
-spec eq_with_threshold(decimal(), decimal(), decimal()) -> boolean().
eq_with_threshold(A, B, Threshold) ->
case compare_with_threshold(A, B, Threshold) of
{ok, eq} ->
true;
_ ->
false
end.
-file("src/dee/decimal.gleam", 1953).
?DOC(
" Returns `True` if the given number is zero, otherwise `False`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" is_zero(zero())\n"
" // -> True\n"
"\n"
" is_zero(from_int(1))\n"
" // -> False\n"
" ```\n"
).
-spec is_zero(decimal()) -> boolean().
is_zero(D) ->
case erlang:element(3, D) of
{finite, Coef} ->
big_is_zero(Coef);
_ ->
false
end.
-file("src/dee/decimal.gleam", 1974).
?DOC(
" Returns `True` when the given decimal has no significant digits after\n"
" the decimal point. Returns `False` for `NaN` and `Infinity`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(d) = from_string(\"1.00\")\n"
" is_integer(d)\n"
" // -> True\n"
"\n"
" let assert Ok(d) = from_string(\"1.10\")\n"
" is_integer(d)\n"
" // -> False\n"
" ```\n"
).
-spec is_integer(decimal()) -> boolean().
is_integer(D) ->
case erlang:element(3, D) of
na_n ->
false;
infinity ->
false;
{finite, Coef} ->
case erlang:element(4, D) >= 0 of
true ->
true;
false ->
Divisor = big_pow10(- erlang:element(4, D)),
big_is_zero(bigi_ffi:remainder(Coef, Divisor))
end
end.
-file("src/dee/decimal.gleam", 2028).
-spec div_int_finite(
sign(),
bigi:big_int(),
integer(),
sign(),
bigi:big_int(),
integer()
) -> decimal().
div_int_finite(Sa, Ca, Ea, Sb, Cb, Eb) ->
Result_sign = mult_signs(Sa, Sb),
Exp_diff = Ea - Eb,
{Aligned_a, Aligned_b} = case Exp_diff >= 0 of
true ->
{bigi_ffi:multiply(Ca, big_pow10(Exp_diff)), Cb};
false ->
{Ca, bigi_ffi:multiply(Cb, big_pow10(- Exp_diff))}
end,
Quotient = bigi_ffi:divide(Aligned_a, Aligned_b),
{decimal, Result_sign, {finite, Quotient}, 0}.
-file("src/dee/decimal.gleam", 2002).
?DOC(
" Divides two numbers and returns the integer part (truncated toward zero).\n"
" Division by zero returns `NaN`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" divide_integer(from_int(5), from_int(2)) |> to_string\n"
" // -> \"2\"\n"
" ```\n"
).
-spec divide_integer(decimal(), decimal()) -> decimal().
divide_integer(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, _} ->
nan();
{_, na_n} ->
nan();
{infinity, infinity} ->
nan();
{_, {finite, Cb}} ->
gleam@bool:guard(
big_is_zero(Cb),
nan(),
fun() -> case erlang:element(3, A) of
infinity ->
Result_sign = mult_signs(
erlang:element(2, A),
erlang:element(2, B)
),
{decimal, Result_sign, infinity, 0};
{finite, Ca} ->
gleam@bool:guard(
big_is_zero(Ca),
zero(),
fun() ->
div_int_finite(
erlang:element(2, A),
Ca,
erlang:element(4, A),
erlang:element(2, B),
Cb,
erlang:element(4, B)
)
end
);
_ ->
nan()
end end
);
{_, infinity} ->
Result_sign@1 = mult_signs(
erlang:element(2, A),
erlang:element(2, B)
),
{decimal, Result_sign@1, {finite, bigi_ffi:zero()}, 0}
end.
-file("src/dee/decimal.gleam", 2080).
-spec rem_finite(sign(), bigi:big_int(), integer(), bigi:big_int(), integer()) -> decimal().
rem_finite(Sa, Ca, Ea, Cb, Eb) ->
Exp_diff = Ea - Eb,
{Aligned_a, Aligned_b, Result_exp} = case Exp_diff >= 0 of
true ->
{bigi_ffi:multiply(Ca, big_pow10(Exp_diff)), Cb, Eb};
false ->
{Ca, bigi_ffi:multiply(Cb, big_pow10(- Exp_diff)), Ea}
end,
Remainder = bigi_ffi:remainder(Aligned_a, Aligned_b),
{decimal, Sa, {finite, Remainder}, Result_exp}.
-file("src/dee/decimal.gleam", 2060).
?DOC(
" Returns the remainder of integer division of two numbers. The result\n"
" will have the sign of the first number. Division by zero returns `NaN`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" remainder(from_int(5), from_int(2)) |> to_string\n"
" // -> \"1\"\n"
" ```\n"
).
-spec remainder(decimal(), decimal()) -> decimal().
remainder(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, _} ->
nan();
{_, na_n} ->
nan();
{_, {finite, Cb}} ->
gleam@bool:guard(
big_is_zero(Cb),
nan(),
fun() -> case erlang:element(3, A) of
infinity ->
nan();
{finite, Ca} ->
gleam@bool:guard(
big_is_zero(Ca),
zero(),
fun() ->
rem_finite(
erlang:element(2, A),
Ca,
erlang:element(4, A),
Cb,
erlang:element(4, B)
)
end
);
_ ->
nan()
end end
);
{infinity, _} ->
nan();
{_, infinity} ->
A
end.
-file("src/dee/decimal.gleam", 2128).
-spec div_rem_finite(
sign(),
bigi:big_int(),
integer(),
sign(),
bigi:big_int(),
integer()
) -> {decimal(), decimal()}.
div_rem_finite(Sa, Ca, Ea, Sb, Cb, Eb) ->
Result_sign = mult_signs(Sa, Sb),
Exp_diff = Ea - Eb,
{Aligned_a, Aligned_b, Result_exp} = case Exp_diff >= 0 of
true ->
{bigi_ffi:multiply(Ca, big_pow10(Exp_diff)), Cb, Eb};
false ->
{Ca, bigi_ffi:multiply(Cb, big_pow10(- Exp_diff)), Ea}
end,
Quotient = bigi_ffi:divide(Aligned_a, Aligned_b),
Remainder = bigi_ffi:remainder(Aligned_a, Aligned_b),
{{decimal, Result_sign, {finite, Quotient}, 0},
{decimal, Sa, {finite, Remainder}, Result_exp}}.
-file("src/dee/decimal.gleam", 2108).
?DOC(
" Integer division of two numbers and the remainder. Returns both the\n"
" quotient and remainder as a tuple. Should be used when both\n"
" `divide_integer` and `remainder` are needed. Division by zero returns\n"
" `#(nan(), nan())`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let #(q, r) = divide_remainder(from_int(5), from_int(2))\n"
" to_string(q)\n"
" // -> \"2\"\n"
" to_string(r)\n"
" // -> \"1\"\n"
" ```\n"
).
-spec divide_remainder(decimal(), decimal()) -> {decimal(), decimal()}.
divide_remainder(A, B) ->
case {erlang:element(3, A), erlang:element(3, B)} of
{na_n, _} ->
{nan(), nan()};
{_, na_n} ->
{nan(), nan()};
{_, {finite, Cb}} ->
gleam@bool:guard(
big_is_zero(Cb),
{nan(), nan()},
fun() -> case erlang:element(3, A) of
infinity ->
{nan(), nan()};
{finite, Ca} ->
gleam@bool:guard(
big_is_zero(Ca),
{zero(), zero()},
fun() ->
div_rem_finite(
erlang:element(2, A),
Ca,
erlang:element(4, A),
erlang:element(2, B),
Cb,
erlang:element(4, B)
)
end
);
_ ->
{nan(), nan()}
end end
);
{infinity, _} ->
{nan(), nan()};
{_, infinity} ->
{zero(), A}
end.