dimitarvp

dimitarvp

Match floating point number in function head?

I am working on a protocol encoder & decoder. It has the quirk that if you wish to encode the maximum possible 64-bit floating point value then you don’t serialize it as all other values (namely the non-scientific full string representation) but only as a singular byte with the value zero.

Poking around on the net, and double-checking with Rust, I found that the value representing the maximum f64 has the byte representation of 0x7FEFFFFFFFFFFFFF. So we can decode it like so:

iex(1)> <<f::float-big-64>> = <<0x7FEFFFFFFFFFFFFF::unsigned-big-integer-64>>
<<127, 239, 255, 255, 255, 255, 255, 255>>
iex(2)> f
1.7976931348623157e308

Which looks (near-)identical to what Rust prints when you execute println!("{}", f64::MAX) in the Rust playground. So far, so good. But now I want to have an encoding function that recognizes the special value in its head and I can’t achieve it.

Obviously this does NOT work:

defmodule Codec do
  def encode_double(0x7FEFFFFFFFFFFFFF), do: <<0>>

  def encode_double(x) when is_float(x) do
    textual_float = Decimal.from_float(x) |> Decimal.to_string(:normal)
    textual_float <> <<0>>
  end
end

And this works just fine:

defmodule Codec do
  def encode_double(x) when is_float(x) do
    case <<x::float-big-64>> do
      # If the value is f64::MAX i.e. `0x7FEFFFFFFFFFFFFF`
      <<127, 239, 255, 255, 255, 255, 255, 255>> ->
        <<0>>

      _ ->
        textual_float = Decimal.from_float(x) |> Decimal.to_string(:normal)
        textual_float <> <<0>>
    end
  end
end

In the REPL:

iex(3)> Codec.encode_double f
<<0>>
iex(4)> Codec.encode_double 3.14
<<51, 46, 49, 52, 0>>

Is there a way to match on that special floating-point value in the function head? Mind you, that value must pass the is_float guard; if it wasn’t for that requirement I could have just used the byte array value in the function head.

Marked As Solved

al2o3cr

al2o3cr

You’d need to write the specific value out:

defmodule Blah do
  def foo(1.7976931348623157e308), do: :nope
  def foo(_x), do: :ok
end

<<f::float-big-64>> = <<0x7FEFFFFFFFFFFFFF::unsigned-big-integer-64>>
Blah.foo(f) # => :nope

<<f2::float-big-64>> = <<0x7FEFFFFFFFFFFFFE::unsigned-big-integer-64>>
Blah.foo(f2) # => :ok

IMO that format seems very strange; there’s a whole category of floats sitting right there (the NaNs / 0x7FF...s) that make better sentinel values.

Where Next?

Popular in Questions Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement