Files
n8n-openai-adapter/lib/elixir/lib/float.ex
T

84 lines
2.5 KiB
Elixir

defmodule Float do
@moduledoc """
Functions for working with floating point numbers.
"""
@doc """
Parses a binary into a float.
If successful, returns a tuple of the form `{ float, remainder_of_binary }`.
Otherwise `:error`.
## Examples
iex> Float.parse("34")
{34.0,""}
iex> Float.parse("34.25")
{34.25,""}
iex> Float.parse("56.5xyz")
{56.5,"xyz"}
iex> Float.parse("pi")
:error
"""
@spec parse(binary) :: { float, binary } | :error
def parse(binary) when is_binary(binary) do
case Integer.parse binary do
:error -> :error
{ integer_part, after_integer } -> parse after_integer, integer_part
end
end
# Dot followed by digit is required afterwards or we are done
defp parse(<< ?., char, rest :: binary >>, int) when char in ?0..?9 do
parse(rest, char - ?0, 1, int)
end
defp parse(rest, int) do
{ :erlang.float(int), rest }
end
# Handle decimal points
defp parse(<< char, rest :: binary >>, float, decimal, int) when char in ?0..?9 do
parse rest, 10 * float + (char - ?0), decimal + 1, int
end
defp parse(<< ?e, after_e :: binary >>, float, decimal, int) do
case Integer.parse after_e do
:error ->
# Note we rebuild the binary here instead of breaking it apart at
# the function clause because the current approach copies a binary
# just on this branch. If we broke it apart in the function clause,
# the copy would happen when calling Integer.parse/1.
{ floatify(int, float, decimal), << ?e, after_e :: binary >> }
{ exponential, after_exponential } ->
{ floatify(int, float, decimal, exponential), after_exponential }
end
end
defp parse(bitstring, float, decimal, int) do
{ floatify(int, float, decimal), bitstring }
end
defp floatify(int, float, decimal, exponential // 0) do
multiplier = if int < 0, do: -1.0, else: 1.0
# Try to ensure the minimum amount of rounding errors
result = multiplier * (abs(int) * :math.pow(10, decimal) + float) * :math.pow(10, exponential - decimal)
# Try avoiding stuff like this:
# iex(1)> 0.0001 * 75
# 0.007500000000000001
# Due to IEEE 754 floating point standard
# http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
final_decimal_places = decimal - exponential
if final_decimal_places > 0 do
decimal_power_round = :math.pow(10, final_decimal_places)
trunc(result * decimal_power_round) / decimal_power_round
else
result
end
end
end