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.