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.







