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
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.
Last Post!
al2o3cr
The next-smallest possible floating point value is 1.7976931348623155e+308 to the same precision - 1.99584030953472e292 smaller. A parser that can’t tell the difference is broken! ![]()
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









