Current section

Files

Jump to
ieee_float src ieee_float.erl
Raw

src/ieee_float.erl

-module(ieee_float).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([finite/1, positive_infinity/0, negative_infinity/0, nan/0, is_finite/1, is_nan/1, to_finite/1, to_string/1, parse/1, to_bytes_32_le/1, from_bytes_32_le/1, to_bytes_32_be/1, from_bytes_32_be/1, to_bytes_64_le/1, from_bytes_64_le/1, to_bytes_64_be/1, from_bytes_64_be/1, absolute_value/1, add/2, ceiling/1, compare/2, divide/2, floor/1, max/2, min/2, clamp/3, multiply/2, negate/1, random/0, round/1, subtract/2]).
-export_type([ieee_float/0, sign/0]).
-opaque ieee_float() :: {finite, float()} | {infinite, sign()} | na_n.
-type sign() :: positive | negative.
-spec finite(float()) -> ieee_float().
finite(F) ->
{finite, F}.
-spec positive_infinity() -> ieee_float().
positive_infinity() ->
{infinite, positive}.
-spec negative_infinity() -> ieee_float().
negative_infinity() ->
{infinite, negative}.
-spec nan() -> ieee_float().
nan() ->
na_n.
-spec is_finite(ieee_float()) -> boolean().
is_finite(F) ->
case F of
{finite, _} ->
true;
_ ->
false
end.
-spec is_nan(ieee_float()) -> boolean().
is_nan(F) ->
F =:= na_n.
-spec to_finite(ieee_float()) -> {ok, float()} | {error, nil}.
to_finite(F) ->
case F of
{finite, Value} ->
{ok, Value};
_ ->
{error, nil}
end.
-spec to_string(ieee_float()) -> binary().
to_string(F) ->
case F of
{finite, F@1} ->
gleam@float:to_string(F@1);
{infinite, positive} ->
<<"Infinity"/utf8>>;
{infinite, negative} ->
<<"-Infinity"/utf8>>;
na_n ->
<<"NaN"/utf8>>
end.
-spec parse(binary()) -> ieee_float().
parse(S) ->
case gleam@string:trim(S) of
<<"Infinity"/utf8>> ->
{infinite, positive};
<<"-Infinity"/utf8>> ->
{infinite, negative};
S@1 ->
case gleam@float:parse(S@1) of
{ok, F} ->
{finite, F};
_ ->
na_n
end
end.
-spec to_bytes_32_le(ieee_float()) -> bitstring().
to_bytes_32_le(F) ->
case F of
{finite, F@1} ->
<<F@1:32/float-little>>;
{infinite, positive} ->
<<16#7F800000:32/little>>;
{infinite, negative} ->
<<16#FF800000:32/little>>;
na_n ->
<<16#7FC00000:32/little>>
end.
-spec from_bytes_32_le(bitstring()) -> ieee_float().
from_bytes_32_le(Bytes) ->
case Bytes of
<<Value:32/float-little>> ->
{finite, Value};
<<16#7F800000:32/little>> ->
{infinite, positive};
<<16#FF800000:32/little>> ->
{infinite, negative};
_ ->
na_n
end.
-spec to_bytes_32_be(ieee_float()) -> bitstring().
to_bytes_32_be(F) ->
case F of
{finite, F@1} ->
<<F@1:32/float-big>>;
{infinite, positive} ->
<<16#7F800000:32/big>>;
{infinite, negative} ->
<<16#FF800000:32/big>>;
na_n ->
<<16#7FC00000:32/big>>
end.
-spec from_bytes_32_be(bitstring()) -> ieee_float().
from_bytes_32_be(Bytes) ->
case Bytes of
<<Value:32/float-big>> ->
{finite, Value};
<<16#7F800000:32/big>> ->
{infinite, positive};
<<16#FF800000:32/big>> ->
{infinite, negative};
_ ->
na_n
end.
-spec to_bytes_64_le(ieee_float()) -> bitstring().
to_bytes_64_le(F) ->
case F of
{finite, F@1} ->
<<F@1:64/float-little>>;
{infinite, positive} ->
<<16#7FF0000000000000:64/little>>;
{infinite, negative} ->
<<16#FFF0000000000000:64/little>>;
na_n ->
<<16#7FF8000000000000:64/little>>
end.
-spec from_bytes_64_le(bitstring()) -> ieee_float().
from_bytes_64_le(Bytes) ->
case Bytes of
<<Value:64/float-little>> ->
{finite, Value};
<<16#7FF0000000000000:64/little>> ->
{infinite, positive};
<<16#FFF0000000000000:64/little>> ->
{infinite, negative};
_ ->
na_n
end.
-spec to_bytes_64_be(ieee_float()) -> bitstring().
to_bytes_64_be(F) ->
case F of
{finite, F@1} ->
<<F@1:64/float-big>>;
{infinite, positive} ->
<<16#7FF0000000000000:64/big>>;
{infinite, negative} ->
<<16#FFF0000000000000:64/big>>;
na_n ->
<<16#7FF8000000000000:64/big>>
end.
-spec from_bytes_64_be(bitstring()) -> ieee_float().
from_bytes_64_be(Bytes) ->
case Bytes of
<<Value:64/float-big>> ->
{finite, Value};
<<16#7FF0000000000000:64/big>> ->
{infinite, positive};
<<16#FFF0000000000000:64/big>> ->
{infinite, negative};
_ ->
na_n
end.
-spec rescue_bad_arith(fun(() -> OCA)) -> {ok, OCA} | {error, nil}.
rescue_bad_arith(Do) ->
case gleam_erlang_ffi:rescue(Do) of
{ok, R} ->
{ok, R};
{error, {errored, Reason}} ->
case begin
_pipe = gleam_erlang_ffi:atom_from_dynamic(Reason),
gleam@result:map(_pipe, fun erlang:atom_to_binary/1)
end
=:= {ok, <<"badarith"/utf8>>} of
true ->
{error, nil};
false ->
erlang:error(#{gleam_error => panic,
message => (<<"Unexpected error in float operation: "/utf8,
(gleam@string:inspect(Reason))/binary>>),
module => <<"ieee_float"/utf8>>,
function => <<"rescue_bad_arith"/utf8>>,
line => 227})
end;
{error, E} ->
erlang:error(#{gleam_error => panic,
message => (<<"Unexpected error in float operation: "/utf8,
(gleam@string:inspect(E))/binary>>),
module => <<"ieee_float"/utf8>>,
function => <<"rescue_bad_arith"/utf8>>,
line => 233})
end.
-spec absolute_value(ieee_float()) -> ieee_float().
absolute_value(F) ->
case F of
{finite, F@1} ->
_pipe = F@1,
_pipe@1 = gleam@float:absolute_value(_pipe),
{finite, _pipe@1};
{infinite, _} ->
{infinite, positive};
na_n ->
na_n
end.
-spec add(ieee_float(), ieee_float()) -> ieee_float().
add(A, B) ->
case {A, B} of
{{finite, A@1}, {finite, B@1}} ->
case rescue_bad_arith(fun() -> {finite, A@1 + B@1} end) of
{ok, F} ->
F;
{error, nil} ->
case {A@1 >= +0.0, B@1 >= +0.0} of
{true, true} ->
{infinite, positive};
{false, false} ->
{infinite, negative};
{_, _} ->
erlang:error(#{gleam_error => panic,
message => <<"Unexpected error in ieee_float.add"/utf8>>,
module => <<"ieee_float"/utf8>>,
function => <<"add"/utf8>>,
line => 260})
end
end;
{{infinite, Sign}, {finite, _}} ->
{infinite, Sign};
{{finite, _}, {infinite, Sign@1}} ->
{infinite, Sign@1};
{{infinite, positive}, {infinite, positive}} ->
{infinite, positive};
{{infinite, negative}, {infinite, negative}} ->
{infinite, negative};
{{infinite, positive}, {infinite, negative}} ->
na_n;
{{infinite, negative}, {infinite, positive}} ->
na_n;
{na_n, _} ->
na_n;
{_, na_n} ->
na_n
end.
-spec ceiling(ieee_float()) -> ieee_float().
ceiling(F) ->
case F of
{finite, F@1} ->
_pipe = F@1,
_pipe@1 = gleam@float:ceiling(_pipe),
{finite, _pipe@1};
_ ->
F
end.
-spec compare(ieee_float(), ieee_float()) -> {ok, gleam@order:order()} |
{error, nil}.
compare(A, B) ->
case {A, B} of
{{finite, A@1}, {finite, B@1}} ->
{ok, gleam@float:compare(A@1, B@1)};
{{finite, _}, {infinite, positive}} ->
{ok, lt};
{{infinite, positive}, {finite, _}} ->
{ok, gt};
{{finite, _}, {infinite, negative}} ->
{ok, gt};
{{infinite, negative}, {finite, _}} ->
{ok, lt};
{{infinite, negative}, {infinite, negative}} ->
{ok, eq};
{{infinite, negative}, {infinite, positive}} ->
{ok, lt};
{{infinite, positive}, {infinite, negative}} ->
{ok, gt};
{{infinite, positive}, {infinite, positive}} ->
{ok, eq};
{na_n, _} ->
{error, nil};
{_, na_n} ->
{error, nil}
end.
-spec divide(ieee_float(), ieee_float()) -> ieee_float().
divide(A, B) ->
case {A, B} of
{na_n, _} ->
na_n;
{_, na_n} ->
na_n;
{{finite, +0.0}, {finite, +0.0}} ->
na_n;
{{finite, +0.0}, {finite, -0.0}} ->
na_n;
{{finite, -0.0}, {finite, +0.0}} ->
na_n;
{{finite, -0.0}, {finite, -0.0}} ->
na_n;
{{finite, A@1}, {finite, +0.0}} ->
case A@1 < +0.0 of
true ->
{infinite, negative};
false ->
{infinite, positive}
end;
{{finite, A@2}, {finite, -0.0}} ->
case A@2 < +0.0 of
true ->
{infinite, positive};
false ->
{infinite, negative}
end;
{{finite, A@3}, {finite, B@1}} ->
case rescue_bad_arith(fun() -> {finite, case B@1 of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> A@3 / Gleam@denominator
end} end) of
{ok, F} ->
F;
{error, nil} ->
case (A@3 >= +0.0) =:= (B@1 >= +0.0) of
true ->
{infinite, positive};
false ->
{infinite, negative}
end
end;
{{finite, A@4}, {infinite, positive}} ->
case A@4 >= +0.0 of
true ->
{finite, +0.0};
false ->
{finite, -0.0}
end;
{{finite, A@5}, {infinite, negative}} ->
case A@5 >= +0.0 of
true ->
{finite, -0.0};
false ->
{finite, +0.0}
end;
{{infinite, positive}, {finite, B@2}} ->
case B@2 >= +0.0 of
true ->
{infinite, positive};
false ->
{infinite, negative}
end;
{{infinite, negative}, {finite, B@3}} ->
case B@3 >= +0.0 of
true ->
{infinite, negative};
false ->
{infinite, positive}
end;
{{infinite, _}, {infinite, _}} ->
na_n
end.
-spec floor(ieee_float()) -> ieee_float().
floor(F) ->
case F of
{finite, F@1} ->
_pipe = F@1,
_pipe@1 = gleam@float:floor(_pipe),
{finite, _pipe@1};
_ ->
F
end.
-spec max(ieee_float(), ieee_float()) -> ieee_float().
max(A, B) ->
case {A, B} of
{{finite, A@1}, {finite, B@1}} ->
_pipe = gleam@float:max(A@1, B@1),
{finite, _pipe};
{{infinite, positive}, _} ->
{infinite, positive};
{_, {infinite, positive}} ->
{infinite, positive};
{{infinite, negative}, A@2} ->
A@2;
{A@2, {infinite, negative}} ->
A@2;
{na_n, _} ->
na_n;
{_, na_n} ->
na_n
end.
-spec min(ieee_float(), ieee_float()) -> ieee_float().
min(A, B) ->
case {A, B} of
{{finite, A@1}, {finite, B@1}} ->
_pipe = gleam@float:min(A@1, B@1),
{finite, _pipe};
{{infinite, negative}, _} ->
{infinite, negative};
{_, {infinite, negative}} ->
{infinite, negative};
{{infinite, positive}, A@2} ->
A@2;
{A@2, {infinite, positive}} ->
A@2;
{na_n, _} ->
na_n;
{_, na_n} ->
na_n
end.
-spec clamp(ieee_float(), ieee_float(), ieee_float()) -> ieee_float().
clamp(F, Min_bound, Max_bound) ->
_pipe = F,
_pipe@1 = min(_pipe, Max_bound),
max(_pipe@1, Min_bound).
-spec multiply(ieee_float(), ieee_float()) -> ieee_float().
multiply(A, B) ->
case {A, B} of
{na_n, _} ->
na_n;
{_, na_n} ->
na_n;
{{finite, A@1}, {finite, B@1}} ->
case rescue_bad_arith(fun() -> {finite, A@1 * B@1} end) of
{ok, F} ->
F;
{error, nil} ->
case (A@1 >= +0.0) =:= (B@1 >= +0.0) of
true ->
{infinite, positive};
false ->
{infinite, negative}
end
end;
{{infinite, positive}, {finite, F@1}} ->
case F@1 >= +0.0 of
true ->
{infinite, positive};
false ->
{infinite, negative}
end;
{{finite, F@1}, {infinite, positive}} ->
case F@1 >= +0.0 of
true ->
{infinite, positive};
false ->
{infinite, negative}
end;
{{infinite, negative}, {finite, F@2}} ->
case F@2 >= +0.0 of
true ->
{infinite, negative};
false ->
{infinite, positive}
end;
{{finite, F@2}, {infinite, negative}} ->
case F@2 >= +0.0 of
true ->
{infinite, negative};
false ->
{infinite, positive}
end;
{{infinite, A@2}, {infinite, B@2}} ->
case A@2 =:= B@2 of
true ->
{infinite, positive};
false ->
{infinite, negative}
end
end.
-spec negate(ieee_float()) -> ieee_float().
negate(F) ->
case F of
{finite, F@1} ->
_pipe = F@1,
_pipe@1 = gleam@float:negate(_pipe),
{finite, _pipe@1};
{infinite, positive} ->
{infinite, negative};
{infinite, negative} ->
{infinite, positive};
na_n ->
na_n
end.
-spec random() -> ieee_float().
random() ->
{finite, rand:uniform()}.
-spec round(ieee_float()) -> {ok, integer()} | {error, nil}.
round(F) ->
case F of
{finite, F@1} ->
_pipe = F@1,
_pipe@1 = gleam@float:round(_pipe),
{ok, _pipe@1};
_ ->
{error, nil}
end.
-spec subtract(ieee_float(), ieee_float()) -> ieee_float().
subtract(A, B) ->
add(A, negate(B)).